From f2d0fbe67a0d32ae59bd05ddccafe9667327bde2 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 22 Jul 2022 13:31:37 +0200 Subject: [PATCH 01/54] removing JHipster --- .editorconfig | 4 - .huskyrc | 5 - .jhipster/Asset.json | 61 - .jhipster/Customer.json | 154 - .jhipster/Membership.json | 66 - .jhipster/SepaMandate.json | 84 - .jhipster/Share.json | 61 - .jhipster/UserRoleAssignment.json | 47 - .prettierignore | 3 - .prettierrc | 12 - .yo-rc.json | 38 - Glossary.md | 18 + JHIPSTER.md | 196 - Jenkinsfile | 57 - README.md | 363 +- ...2022-07-18.row-level-security-mechanism.md | 160 + angular.json | 39 - build-cucumber.gradle | 24 - build-jacoco.gradle | 84 - build-pitest.gradle | 36 - build-spotless.gradle | 15 - build.gradle | 297 - cfg/spotless/eclipse_formatter.xml | 315 - cfg/spotless/hsadminng.importorder | 8 - cfg/spotless/javascript_formatters.xml | 267 - gradle.properties | 53 - gradle/docker.gradle | 35 - gradle/profile_dev.gradle | 72 - gradle/profile_prod.gradle | 63 - gradle/sonar.gradle | 47 - gradle/swagger.gradle | 28 - gradle/wrapper/gradle-wrapper.jar | Bin 54413 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 6 - gradle/zipkin.gradle | 3 - gradlew | 172 - gradlew.bat | 84 - package-lock.json | 18180 ---------------- package.json | 126 - postcss.config.js | 5 - proxy.conf.json | 7 - settings.gradle | 1 - sql/00-util.sql | 38 + sql/10-rbac-base.sql | 657 + sql/11-rbac-view.sql | 89 + sql/19-rbac-tests.sql | 50 + sql/20-hs-base.sql | 18 + sql/21-hs-customer.sql | 134 + sql/22-hs-packages.sql | 117 + sql/23-hs-unixuser.sql | 106 + sql/24-hs-domain.sql | 98 + sql/25-hs-emailaddress.sql | 122 + sql/29-hs-statistics.sql | 24 + sql/examples.sql | 62 + sql/historization.sql | 166 + sql/history-demo.sql | 105 - sql/rbac-statistics.sql | 18 + sql/rbac.md | 301 + src/test/features/gitkeep => sql/rbac.pdf | 0 sql/rbac.sql | 2 + src/main/docker/.dockerignore | 14 - src/main/docker/Dockerfile | 20 - src/main/docker/app.yml | 15 - src/main/docker/entrypoint.sh | 4 - src/main/docker/jenkins.yml | 15 - src/main/docker/postgresql.yml | 11 - src/main/docker/sonar.yml | 7 - src/main/docker/swagger-editor.yml | 6 - .../hsadminng/ApplicationWebXml.java | 23 - .../hostsharing/hsadminng/HsadminNgApp.java | 111 - .../hsadminng/aop/logging/LoggingAspect.java | 116 - .../config/ApplicationProperties.java | 15 - .../hsadminng/config/AsyncConfiguration.java | 60 - .../hsadminng/config/CacheConfiguration.java | 45 - .../config/CloudDatabaseConfiguration.java | 28 - .../hsadminng/config/Constants.java | 18 - .../config/DatabaseConfiguration.java | 60 - .../config/DateTimeFormatConfiguration.java | 21 - .../hsadminng/config/DefaultProfileUtil.java | 52 - .../config/JacksonConfiguration.java | 64 - .../config/LiquibaseConfiguration.java | 52 - .../hsadminng/config/LocaleConfiguration.java | 28 - .../config/LoggingAspectConfiguration.java | 20 - .../config/LoggingConfiguration.java | 160 - .../config/SecurityConfiguration.java | 128 - .../hsadminng/config/WebConfigurer.java | 172 - .../config/audit/AuditEventConverter.java | 90 - .../hsadminng/config/audit/package-info.java | 4 - .../hsadminng/config/package-info.java | 4 - .../domain/AbstractAuditingEntity.java | 82 - .../hostsharing/hsadminng/domain/Asset.java | 184 - .../hsadminng/domain/Authority.java | 61 - .../hsadminng/domain/Customer.java | 408 - .../hsadminng/domain/Membership.java | 238 - .../domain/PersistentAuditEvent.java | 113 - .../hsadminng/domain/SepaMandate.java | 250 - .../hostsharing/hsadminng/domain/Share.java | 183 - .../hostsharing/hsadminng/domain/User.java | 240 - .../hsadminng/domain/UserRoleAssignment.java | 214 - .../domain/enumeration/AssetAction.java | 14 - .../domain/enumeration/CustomerKind.java | 10 - .../domain/enumeration/ShareAction.java | 10 - .../domain/enumeration/VatRegion.java | 11 - .../hsadminng/domain/package-info.java | 4 - .../liquibase/ReplaceCustomChange.java | 98 - .../hsadminng/repository/AssetRepository.java | 16 - .../repository/AuthorityRepository.java | 12 - .../CustomAuditEventRepository.java | 94 - .../repository/CustomerRepository.java | 16 - .../repository/MembershipRepository.java | 22 - .../PersistenceAuditEventRepository.java | 30 - .../repository/SepaMandateRepository.java | 16 - .../hsadminng/repository/ShareRepository.java | 16 - .../hsadminng/repository/UserRepository.java | 49 - .../UserRoleAssignmentRepository.java | 32 - .../hsadminng/repository/package-info.java | 4 - .../security/AuthoritiesConstants.java | 21 - .../security/DomainUserDetailsService.java | 67 - .../hsadminng/security/SecurityUtils.java | 88 - .../security/SpringSecurityAuditorAware.java | 21 - .../security/UserNotActivatedException.java | 20 - .../hsadminng/security/jwt/JWTConfigurer.java | 22 - .../hsadminng/security/jwt/JWTFilter.java | 50 - .../hsadminng/security/jwt/TokenProvider.java | 123 - .../hsadminng/security/package-info.java | 4 - .../hsadminng/service/AssetQueryService.java | 120 - .../hsadminng/service/AssetService.java | 100 - .../hsadminng/service/AssetValidator.java | 70 - .../hsadminng/service/AuditEventService.java | 53 - .../service/CustomerQueryService.java | 164 - .../hsadminng/service/CustomerService.java | 84 - .../hsadminng/service/IdToDtoResolver.java | 9 - .../hsadminng/service/MailService.java | 115 - .../service/MembershipQueryService.java | 135 - .../hsadminng/service/MembershipService.java | 103 - .../service/MembershipValidator.java | 33 - .../service/SepaMandateQueryService.java | 138 - .../hsadminng/service/SepaMandateService.java | 94 - .../hsadminng/service/ShareQueryService.java | 120 - .../hsadminng/service/ShareService.java | 103 - .../hsadminng/service/ShareValidator.java | 44 - .../UserRoleAssignmentQueryService.java | 112 - .../service/UserRoleAssignmentService.java | 111 - .../hsadminng/service/UserService.java | 302 - .../service/accessfilter/AccessFor.java | 18 - .../service/accessfilter/AccessMappings.java | 13 - .../service/accessfilter/EntityTypeId.java | 20 - .../accessfilter/JSonAccessFilter.java | 149 - .../service/accessfilter/JSonFieldReader.java | 19 - .../service/accessfilter/JSonFieldWriter.java | 23 - .../JsonDeserializerWithAccessFilter.java | 288 - .../JsonSerializerWithAccessFilter.java | 138 - .../service/accessfilter/ParentId.java | 21 - .../hsadminng/service/accessfilter/Role.java | 437 - .../service/accessfilter/SelfId.java | 23 - .../hsadminng/service/dto/AssetCriteria.java | 146 - .../hsadminng/service/dto/AssetDTO.java | 174 - .../service/dto/CustomerCriteria.java | 292 - .../hsadminng/service/dto/CustomerDTO.java | 319 - .../hsadminng/service/dto/FluentBuilder.java | 46 - .../service/dto/MembershipCriteria.java | 163 - .../hsadminng/service/dto/MembershipDTO.java | 203 - .../service/dto/PasswordChangeDTO.java | 37 - .../service/dto/SepaMandateCriteria.java | 189 - .../hsadminng/service/dto/SepaMandateDTO.java | 242 - .../hsadminng/service/dto/ShareCriteria.java | 146 - .../hsadminng/service/dto/ShareDTO.java | 173 - .../hsadminng/service/dto/UserDTO.java | 200 - .../dto/UserRoleAssignmentCriteria.java | 110 - .../hsadminng/service/dto/package-info.java | 4 - .../hsadminng/service/mapper/AssetMapper.java | 38 - .../service/mapper/CustomerMapper.java | 43 - .../service/mapper/EntityMapper.java | 22 - .../service/mapper/MembershipMapper.java | 55 - .../service/mapper/SepaMandateMapper.java | 38 - .../hsadminng/service/mapper/ShareMapper.java | 38 - .../hsadminng/service/mapper/UserMapper.java | 81 - .../service/mapper/package-info.java | 4 - .../hsadminng/service/package-info.java | 4 - .../hsadminng/service/util/RandomUtil.java | 53 - .../service/util/ReflectionUtil.java | 218 - .../hsadminng/web/rest/AccountResource.java | 183 - .../hsadminng/web/rest/AssetResource.java | 137 - .../hsadminng/web/rest/AuditResource.java | 79 - .../hsadminng/web/rest/CustomerResource.java | 142 - .../hsadminng/web/rest/LogsResource.java | 38 - .../web/rest/MembershipResource.java | 144 - .../web/rest/SepaMandateResource.java | 144 - .../hsadminng/web/rest/ShareResource.java | 137 - .../hsadminng/web/rest/UserJWTController.java | 73 - .../hsadminng/web/rest/UserResource.java | 188 - .../web/rest/UserRoleAssignmentResource.java | 148 - .../rest/errors/BadRequestAlertException.java | 43 - .../errors/CustomParameterizedException.java | 61 - .../errors/EmailAlreadyUsedException.java | 11 - .../rest/errors/EmailNotFoundException.java | 14 - .../web/rest/errors/ErrorConstants.java | 25 - .../web/rest/errors/ExceptionTranslator.java | 137 - .../web/rest/errors/FieldErrorVM.java | 34 - .../errors/InternalServerErrorException.java | 17 - .../rest/errors/InvalidPasswordException.java | 14 - .../errors/LoginAlreadyUsedException.java | 11 - .../web/rest/errors/package-info.java | 6 - .../hsadminng/web/rest/package-info.java | 4 - .../hsadminng/web/rest/util/HeaderUtil.java | 46 - .../web/rest/util/PaginationUtil.java | 46 - .../web/rest/vm/KeyAndPasswordVM.java | 28 - .../hsadminng/web/rest/vm/LoggerVM.java | 47 - .../hsadminng/web/rest/vm/LoginVM.java | 53 - .../hsadminng/web/rest/vm/ManagedUserVM.java | 37 - .../hsadminng/web/rest/vm/package-info.java | 4 - src/main/jdl/accessrights.jdl | 23 - src/main/jdl/customer.jdl | 90 - src/main/jib/entrypoint.sh | 4 - src/main/resources/.h2.server.properties | 6 - src/main/resources/banner.txt | 10 - src/main/resources/config/application-dev.yml | 117 - .../resources/config/application-h2file.yml | 20 - .../resources/config/application-h2mem.yml | 22 - .../resources/config/application-pgsql.yml | 17 - .../resources/config/application-prod.yml | 132 - src/main/resources/config/application-tls.yml | 20 - src/main/resources/config/application.yml | 143 - .../config/liquibase/authorities.csv | 5 - .../00000000000000_initial_schema.xml | 154 - .../20190507105332_added_entity_Customer.xml | 87 - ...20190507105333_added_entity_Membership.xml | 51 - ...33_added_entity_constraints_Membership.xml | 18 - .../20190507105334_added_entity_Share.xml | 51 - ...7105334_added_entity_constraints_Share.xml | 18 - .../20190507105335_added_entity_Asset.xml | 51 - ...7105335_added_entity_constraints_Asset.xml | 18 - ...0190507105336_added_entity_SepaMandate.xml | 67 - ...6_added_entity_constraints_SepaMandate.xml | 18 - ...105342_added_entity_UserRoleAssignment.xml | 45 - ..._entity_constraints_UserRoleAssignment.xml | 18 - .../changelog/constraints_Membership.xml | 15 - .../constraints_UserRoleAssignment.xml | 15 - .../historicization/historicization.xml | 46 - .../historicization/historicization_Asset.xml | 96 - .../historicization_Customer.xml | 131 - .../historicization_Membership.xml | 95 - .../historicization_SepaMandate.xml | 112 - .../historicization/historicization_Share.xml | 96 - .../historicization/historicization_User.xml | 98 - .../historicization_UserAuthority.xml | 75 - .../historicization_UserRoleAssignment.xml | 87 - .../resources/config/liquibase/master.xml | 56 - .../config/liquibase/sample-data/assets.csv | 58 - .../config/liquibase/sample-data/assets.xml | 31 - .../liquibase/sample-data/customers.csv | 7 - .../liquibase/sample-data/customers.xml | 32 - .../liquibase/sample-data/memberships.csv | 7 - .../liquibase/sample-data/memberships.xml | 32 - .../liquibase/sample-data/sepamandates.csv | 6 - .../liquibase/sample-data/sepamandates.xml | 32 - .../config/liquibase/sample-data/shares.csv | 13 - .../config/liquibase/sample-data/shares.xml | 32 - .../sample-data/user_role_assignments.csv | 17 - .../sample-data/user_role_assignments.xml | 20 - .../config/liquibase/sample-data/users.csv | 24 - .../config/liquibase/sample-data/users.xml | 20 - .../sample-data/users_authorities.csv | 33 - .../sample-data/users_authorities.xml | 20 - src/main/resources/config/liquibase/users.csv | 5 - .../config/liquibase/users_authorities.csv | 7 - src/main/resources/config/tls/keystore.p12 | Bin 2615 -> 0 bytes src/main/resources/i18n/messages.properties | 21 - .../resources/i18n/messages_de.properties | 21 - .../resources/i18n/messages_en.properties | 21 - src/main/resources/idea.gdsl | 90 - src/main/resources/logback-spring.xml | 73 - src/main/resources/swagger/api.yml | 7 - src/main/resources/templates/error.html | 163 - .../templates/mail/activationEmail.html | 25 - .../templates/mail/creationEmail.html | 25 - .../templates/mail/passwordResetEmail.html | 25 - src/main/webapp/404.html | 61 - src/main/webapp/app/account/account.module.ts | 30 - src/main/webapp/app/account/account.route.ts | 12 - .../account/activate/activate.component.html | 17 - .../account/activate/activate.component.ts | 37 - .../app/account/activate/activate.route.ts | 12 - .../app/account/activate/activate.service.ts | 16 - src/main/webapp/app/account/index.ts | 19 - .../password-reset-finish.component.html | 77 - .../finish/password-reset-finish.component.ts | 65 - .../finish/password-reset-finish.route.ts | 12 - .../finish/password-reset-finish.service.ts | 14 - .../init/password-reset-init.component.html | 46 - .../init/password-reset-init.component.ts | 43 - .../init/password-reset-init.route.ts | 12 - .../init/password-reset-init.service.ts | 14 - .../password-strength-bar.component.ts | 85 - .../password/password-strength-bar.css | 24 - .../account/password/password.component.html | 77 - .../account/password/password.component.ts | 46 - .../app/account/password/password.route.ts | 14 - .../app/account/password/password.service.ts | 14 - .../account/register/register.component.html | 124 - .../account/register/register.component.ts | 75 - .../app/account/register/register.route.ts | 12 - .../app/account/register/register.service.ts | 14 - .../account/settings/settings.component.html | 86 - .../account/settings/settings.component.ts | 63 - .../app/account/settings/settings.route.ts | 14 - src/main/webapp/app/admin/admin.module.ts | 54 - src/main/webapp/app/admin/admin.route.ts | 18 - .../app/admin/audits/audit-data.model.ts | 3 - .../webapp/app/admin/audits/audit.model.ts | 5 - .../app/admin/audits/audits.component.html | 52 - .../app/admin/audits/audits.component.ts | 126 - .../webapp/app/admin/audits/audits.route.ts | 17 - .../webapp/app/admin/audits/audits.service.ts | 25 - .../configuration.component.html | 46 - .../configuration/configuration.component.ts | 43 - .../configuration/configuration.route.ts | 11 - .../configuration/configuration.service.ts | 67 - .../webapp/app/admin/docs/docs.component.html | 2 - .../webapp/app/admin/docs/docs.component.ts | 9 - src/main/webapp/app/admin/docs/docs.route.ts | 11 - .../admin/health/health-modal.component.html | 36 - .../admin/health/health-modal.component.ts | 41 - .../app/admin/health/health.component.html | 34 - .../app/admin/health/health.component.ts | 66 - .../webapp/app/admin/health/health.route.ts | 11 - .../webapp/app/admin/health/health.service.ts | 133 - src/main/webapp/app/admin/index.ts | 27 - src/main/webapp/app/admin/logs/log.model.ts | 3 - .../webapp/app/admin/logs/logs.component.html | 28 - .../webapp/app/admin/logs/logs.component.ts | 32 - src/main/webapp/app/admin/logs/logs.route.ts | 11 - .../webapp/app/admin/logs/logs.service.ts | 19 - .../app/admin/metrics/metrics.component.html | 56 - .../app/admin/metrics/metrics.component.ts | 42 - .../webapp/app/admin/metrics/metrics.route.ts | 11 - .../app/admin/metrics/metrics.service.ts | 18 - ...er-management-delete-dialog.component.html | 19 - ...user-management-delete-dialog.component.ts | 29 - .../user-management-detail.component.html | 49 - .../user-management-detail.component.ts | 20 - .../user-management-update.component.html | 124 - .../user-management-update.component.ts | 58 - .../user-management.component.html | 79 - .../user-management.component.ts | 144 - .../user-management/user-management.route.ts | 68 - src/main/webapp/app/app-routing.module.ts | 23 - src/main/webapp/app/app.constants.ts | 8 - src/main/webapp/app/app.main.ts | 14 - src/main/webapp/app/app.module.ts | 72 - .../webapp/app/blocks/config/prod.config.ts | 9 - .../blocks/config/uib-pagination.config.ts | 14 - .../interceptor/auth-expired.interceptor.ts | 25 - .../blocks/interceptor/auth.interceptor.ts | 27 - .../interceptor/errorhandler.interceptor.ts | 25 - .../interceptor/notification.interceptor.ts | 37 - .../webapp/app/core/auth/account.service.ts | 114 - .../webapp/app/core/auth/auth-jwt.service.ts | 59 - src/main/webapp/app/core/auth/csrf.service.ts | 11 - .../app/core/auth/state-storage.service.ts | 46 - .../core/auth/user-route-access-service.ts | 52 - src/main/webapp/app/core/core.module.ts | 24 - src/main/webapp/app/core/index.ts | 13 - .../app/core/language/language.constants.ts | 9 - .../app/core/language/language.helper.ts | 65 - .../app/core/login/login-modal.service.ts | 27 - .../webapp/app/core/login/login.service.ts | 38 - .../webapp/app/core/user/account.model.ts | 12 - src/main/webapp/app/core/user/user.model.ts | 47 - src/main/webapp/app/core/user/user.service.ts | 39 - .../asset/asset-delete-dialog.component.html | 19 - .../asset/asset-delete-dialog.component.ts | 65 - .../asset/asset-detail.component.html | 49 - .../entities/asset/asset-detail.component.ts | 24 - .../asset/asset-update.component.html | 112 - .../entities/asset/asset-update.component.ts | 79 - .../app/entities/asset/asset.component.html | 94 - .../app/entities/asset/asset.component.ts | 141 - .../webapp/app/entities/asset/asset.module.ts | 34 - .../webapp/app/entities/asset/asset.route.ts | 93 - .../app/entities/asset/asset.service.ts | 77 - src/main/webapp/app/entities/asset/index.ts | 6 - .../customer-delete-dialog.component.html | 19 - .../customer-delete-dialog.component.ts | 65 - .../customer/customer-detail.component.html | 83 - .../customer/customer-detail.component.ts | 24 - .../customer/customer-update.component.html | 222 - .../customer/customer-update.component.ts | 51 - .../entities/customer/customer.component.html | 74 - .../entities/customer/customer.component.ts | 122 - .../app/entities/customer/customer.module.ts | 40 - .../app/entities/customer/customer.route.ts | 93 - .../app/entities/customer/customer.service.ts | 74 - .../webapp/app/entities/customer/index.ts | 6 - src/main/webapp/app/entities/entity.module.ts | 39 - .../webapp/app/entities/membership/index.ts | 6 - .../membership-delete-dialog.component.html | 19 - .../membership-delete-dialog.component.ts | 69 - .../membership-detail.component.html | 49 - .../membership/membership-detail.component.ts | 24 - .../membership-update.component.html | 100 - .../membership/membership-update.component.ts | 80 - .../membership/membership.component.html | 84 - .../membership/membership.component.ts | 142 - .../entities/membership/membership.module.ts | 40 - .../entities/membership/membership.route.ts | 93 - .../entities/membership/membership.service.ts | 98 - .../webapp/app/entities/sepa-mandate/index.ts | 6 - .../sepa-mandate-delete-dialog.component.html | 19 - .../sepa-mandate-delete-dialog.component.ts | 72 - .../sepa-mandate-detail.component.html | 65 - .../sepa-mandate-detail.component.ts | 24 - .../sepa-mandate-update.component.html | 147 - .../sepa-mandate-update.component.ts | 81 - .../sepa-mandate/sepa-mandate.component.html | 96 - .../sepa-mandate/sepa-mandate.component.ts | 150 - .../sepa-mandate/sepa-mandate.module.ts | 40 - .../sepa-mandate/sepa-mandate.route.ts | 93 - .../sepa-mandate/sepa-mandate.service.ts | 101 - src/main/webapp/app/entities/share/index.ts | 6 - .../share/share-delete-dialog.component.html | 19 - .../share/share-delete-dialog.component.ts | 65 - .../share/share-detail.component.html | 49 - .../entities/share/share-detail.component.ts | 24 - .../share/share-update.component.html | 108 - .../entities/share/share-update.component.ts | 78 - .../app/entities/share/share.component.html | 90 - .../app/entities/share/share.component.ts | 141 - .../webapp/app/entities/share/share.module.ts | 34 - .../webapp/app/entities/share/share.route.ts | 93 - .../app/entities/share/share.service.ts | 77 - .../entities/user-role-assignment/index.ts | 6 - ...le-assignment-delete-dialog.component.html | 19 - ...role-assignment-delete-dialog.component.ts | 72 - ...user-role-assignment-detail.component.html | 39 - .../user-role-assignment-detail.component.ts | 24 - ...user-role-assignment-update.component.html | 77 - .../user-role-assignment-update.component.ts | 75 - .../user-role-assignment.component.html | 90 - .../user-role-assignment.component.ts | 139 - .../user-role-assignment.module.ts | 45 - .../user-role-assignment.route.ts | 93 - .../user-role-assignment.service.ts | 38 - src/main/webapp/app/home/home.component.html | 41 - src/main/webapp/app/home/home.component.ts | 44 - src/main/webapp/app/home/home.css | 23 - src/main/webapp/app/home/home.module.ts | 12 - src/main/webapp/app/home/home.route.ts | 12 - src/main/webapp/app/home/index.ts | 3 - .../app/layouts/error/error.component.html | 19 - .../app/layouts/error/error.component.ts | 28 - .../webapp/app/layouts/error/error.route.ts | 36 - .../app/layouts/footer/footer.component.html | 3 - .../app/layouts/footer/footer.component.ts | 7 - src/main/webapp/app/layouts/index.ts | 10 - .../app/layouts/main/main.component.html | 11 - .../webapp/app/layouts/main/main.component.ts | 31 - .../layouts/navbar/active-menu.directive.ts | 26 - .../app/layouts/navbar/navbar.component.html | 188 - .../app/layouts/navbar/navbar.component.ts | 79 - src/main/webapp/app/layouts/navbar/navbar.css | 89 - .../webapp/app/layouts/navbar/navbar.route.ts | 9 - .../layouts/profiles/page-ribbon.component.ts | 26 - .../app/layouts/profiles/page-ribbon.css | 32 - .../layouts/profiles/profile-info.model.ts | 6 - .../app/layouts/profiles/profile.service.ts | 40 - src/main/webapp/app/polyfills.ts | 70 - .../app/shared/alert/alert-error.component.ts | 114 - .../app/shared/alert/alert.component.ts | 35 - .../auth/has-any-authority.directive.ts | 42 - .../app/shared/constants/error.constants.ts | 4 - .../app/shared/constants/input.constants.ts | 2 - .../shared/constants/pagination.constants.ts | 3 - src/main/webapp/app/shared/index.ts | 13 - .../language/find-language-from-key.pipe.ts | 13 - .../app/shared/login/login.component.html | 43 - .../app/shared/login/login.component.ts | 87 - .../webapp/app/shared/model/asset.model.ts | 34 - .../webapp/app/shared/model/customer.model.ts | 60 - .../app/shared/model/membership.model.ts | 35 - .../app/shared/model/sepa-mandate.model.ts | 33 - .../webapp/app/shared/model/share.model.ts | 30 - .../model/user-role-assignment.model.ts | 29 - .../webapp/app/shared/shared-common.module.ts | 11 - .../webapp/app/shared/shared-libs.module.ts | 20 - src/main/webapp/app/shared/shared.module.ts | 21 - .../app/shared/util/datepicker-adapter.ts | 21 - .../webapp/app/shared/util/linebreaks-pipe.ts | 11 - .../webapp/app/shared/util/request-util.ts | 18 - .../webapp/app/shared/util/tablefilter.ts | 79 - src/main/webapp/app/vendor.ts | 81 - src/main/webapp/content/css/documentation.css | 3 - src/main/webapp/content/css/global.css | 238 - src/main/webapp/content/css/loading.css | 152 - src/main/webapp/content/css/vendor.css | 2 - .../images/jhipster_family_member_0.svg | 198 - .../jhipster_family_member_0_head-192.png | Bin 13322 -> 0 bytes .../jhipster_family_member_0_head-256.png | Bin 17812 -> 0 bytes .../jhipster_family_member_0_head-384.png | Bin 27723 -> 0 bytes .../jhipster_family_member_0_head-512.png | Bin 37449 -> 0 bytes .../images/jhipster_family_member_1.svg | 9387 -------- .../jhipster_family_member_1_head-192.png | Bin 19660 -> 0 bytes .../jhipster_family_member_1_head-256.png | Bin 29152 -> 0 bytes .../jhipster_family_member_1_head-384.png | Bin 48895 -> 0 bytes .../jhipster_family_member_1_head-512.png | Bin 68803 -> 0 bytes .../images/jhipster_family_member_2.svg | 841 - .../jhipster_family_member_2_head-192.png | Bin 11463 -> 0 bytes .../jhipster_family_member_2_head-256.png | Bin 15638 -> 0 bytes .../jhipster_family_member_2_head-384.png | Bin 23850 -> 0 bytes .../jhipster_family_member_2_head-512.png | Bin 32300 -> 0 bytes .../images/jhipster_family_member_3.svg | 308 - .../jhipster_family_member_3_head-192.png | Bin 13573 -> 0 bytes .../jhipster_family_member_3_head-256.png | Bin 19239 -> 0 bytes .../jhipster_family_member_3_head-384.png | Bin 30253 -> 0 bytes .../jhipster_family_member_3_head-512.png | Bin 41857 -> 0 bytes .../webapp/content/images/logo-jhipster.png | Bin 1085 -> 0 bytes src/main/webapp/favicon.ico | Bin 1574 -> 0 bytes src/main/webapp/i18n/de/activate.json | 9 - src/main/webapp/i18n/de/asset.json | 26 - src/main/webapp/i18n/de/assetAction.json | 13 - src/main/webapp/i18n/de/audits.json | 27 - src/main/webapp/i18n/de/configuration.json | 10 - src/main/webapp/i18n/de/custom-error.json | 23 - src/main/webapp/i18n/de/customer.json | 37 - src/main/webapp/i18n/de/customerKind.json | 9 - src/main/webapp/i18n/de/error.json | 14 - src/main/webapp/i18n/de/global.json | 142 - src/main/webapp/i18n/de/health.json | 28 - src/main/webapp/i18n/de/home.json | 19 - src/main/webapp/i18n/de/login.json | 19 - src/main/webapp/i18n/de/logs.json | 11 - src/main/webapp/i18n/de/membership.json | 28 - src/main/webapp/i18n/de/metrics.json | 93 - src/main/webapp/i18n/de/password.json | 12 - src/main/webapp/i18n/de/register.json | 24 - src/main/webapp/i18n/de/reset.json | 27 - src/main/webapp/i18n/de/sepaMandate.json | 30 - src/main/webapp/i18n/de/sessions.json | 15 - src/main/webapp/i18n/de/settings.json | 32 - src/main/webapp/i18n/de/share.json | 26 - src/main/webapp/i18n/de/shareAction.json | 9 - src/main/webapp/i18n/de/user-management.json | 30 - src/main/webapp/i18n/de/userRole.json | 14 - .../webapp/i18n/de/userRoleAssignment.json | 24 - src/main/webapp/i18n/de/vatRegion.json | 10 - src/main/webapp/i18n/en/activate.json | 9 - src/main/webapp/i18n/en/asset.json | 26 - src/main/webapp/i18n/en/assetAction.json | 13 - src/main/webapp/i18n/en/audits.json | 27 - src/main/webapp/i18n/en/configuration.json | 10 - src/main/webapp/i18n/en/custom-error.json | 23 - src/main/webapp/i18n/en/customer.json | 37 - src/main/webapp/i18n/en/customerKind.json | 9 - src/main/webapp/i18n/en/error.json | 14 - src/main/webapp/i18n/en/global.json | 143 - src/main/webapp/i18n/en/health.json | 28 - src/main/webapp/i18n/en/home.json | 19 - src/main/webapp/i18n/en/login.json | 19 - src/main/webapp/i18n/en/logs.json | 11 - src/main/webapp/i18n/en/membership.json | 28 - src/main/webapp/i18n/en/metrics.json | 102 - src/main/webapp/i18n/en/password.json | 12 - src/main/webapp/i18n/en/register.json | 24 - src/main/webapp/i18n/en/reset.json | 27 - src/main/webapp/i18n/en/sepaMandate.json | 30 - src/main/webapp/i18n/en/sessions.json | 15 - src/main/webapp/i18n/en/settings.json | 32 - src/main/webapp/i18n/en/share.json | 26 - src/main/webapp/i18n/en/shareAction.json | 9 - src/main/webapp/i18n/en/user-management.json | 30 - src/main/webapp/i18n/en/userRole.json | 14 - .../webapp/i18n/en/userRoleAssignment.json | 24 - src/main/webapp/i18n/en/vatRegion.json | 10 - src/main/webapp/index.html | 109 - src/main/webapp/manifest.webapp | 31 - src/main/webapp/robots.txt | 11 - .../swagger-ui/dist/images/throbber.gif | Bin 9257 -> 0 bytes src/main/webapp/swagger-ui/index.html | 166 - src/test/features/user/user.feature | 6 - .../hsadminng/config/WebConfigurerTest.java | 193 - .../config/WebConfigurerTestController.java | 17 - .../timezone/HibernateTimeZoneTest.java | 178 - .../CucumberContextConfiguration.java | 23 - .../hsadminng/cucumber/CucumberTest.java | 13 - .../hsadminng/cucumber/stepdefs/StepDefs.java | 10 - .../cucumber/stepdefs/UserStepDefs.java | 49 - .../ReplaceCustomChangeUnitTest.java | 165 - .../repository/AssetRepositoryIntTest.java | 76 - .../CustomAuditEventRepositoryIntTest.java | 168 - .../repository/CustomerRepositoryIntTest.java | 52 - .../MembershipRepositoryIntTest.java | 104 - .../SepaMandateRepositoryIntTest.java | 62 - .../repository/ShareRepositoryIntTest.java | 75 - .../repository/timezone/DateTimeWrapper.java | 134 - .../timezone/DateTimeWrapperRepository.java | 13 - .../DomainUserDetailsServiceIntTest.java | 128 - .../security/SecurityUtilsUnitTest.java | 74 - .../hsadminng/security/jwt/JWTFilterTest.java | 119 - .../security/jwt/TokenProviderTest.java | 116 - .../service/AssetServiceUnitTest.java | 180 - .../service/AssetValidatorUnitTest.java | 318 - .../hsadminng/service/MailServiceIntTest.java | 192 - .../service/MembershipServiceUnitTest.java | 68 - .../service/MembershipValidatorUnitTest.java | 149 - .../service/ShareServiceUnitTest.java | 178 - .../service/ShareValidatorUnitTest.java | 190 - .../UserRoleAssignmentServiceUnitTest.java | 99 - .../hsadminng/service/UserServiceIntTest.java | 194 - .../accessfilter/JSonAccessFilterTest.java | 34 - .../JSonAccessFilterTestFixture.java | 237 - .../service/accessfilter/JSonBuilder.java | 91 - ...serializationWithAccessFilterUnitTest.java | 579 - ...SerializationWithAccessFilterUnitTest.java | 218 - .../service/accessfilter/RoleUnitTest.java | 195 - .../accessfilter/SecurityContextDouble.java | 57 - .../accessfilter/SecurityContextFake.java | 23 - .../accessfilter/SecurityContextMock.java | 47 - .../dto/AccessMappingsUnitTestBase.java | 237 - .../service/dto/AssetDTOIntTest.java | 229 - .../service/dto/AssetDTOUnitTest.java | 92 - .../service/dto/CustomerDTOUnitTest.java | 188 - .../service/dto/MembershipDTOIntTest.java | 197 - .../service/dto/MembershipDTOUnitTest.java | 108 - .../service/dto/SepaMandateDTOIntTest.java | 206 - .../service/dto/SepaMandateDTOUnitTest.java | 137 - .../service/dto/ShareDTOIntTest.java | 227 - .../service/dto/ShareDTOUnitTest.java | 92 - .../dto/UserRoleAssignmentUnitTest.java | 163 - .../service/mapper/UserMapperTest.java | 151 - .../service/util/ReflectionUtilUnitTest.java | 147 - .../web/rest/AccountResourceIntTest.java | 829 - .../web/rest/AssetResourceIntTest.java | 777 - .../web/rest/AuditResourceIntTest.java | 164 - .../web/rest/CustomerResourceIntTest.java | 1339 -- .../web/rest/LogsResourceIntTest.java | 70 - .../web/rest/MembershipResourceIntTest.java | 837 - .../web/rest/MembershipResourceUnitTest.java | 58 - .../web/rest/SepaMandateResourceIntTest.java | 1009 - .../web/rest/SepaMandateResourceUnitTest.java | 58 - .../web/rest/ShareResourceIntTest.java | 802 - .../hsadminng/web/rest/TestUtil.java | 147 - .../web/rest/UserJWTControllerIntTest.java | 130 - .../web/rest/UserResourceIntTest.java | 622 - .../UserRoleAssignmentResourceIntTest.java | 552 - .../UserRoleAssignmentResourceUnitTest.java | 58 - .../errors/ExceptionTranslatorIntTest.java | 152 - .../ExceptionTranslatorTestController.java | 88 - .../web/rest/util/PaginationUtilUnitTest.java | 45 - src/test/javascript/jest-global-mocks.ts | 15 - src/test/javascript/jest.conf.js | 26 - src/test/javascript/jest.ts | 2 - .../activate/activate.component.spec.ts | 72 - .../password-reset-finish.component.spec.ts | 119 - .../password-reset-init.component.spec.ts | 110 - .../password-strength-bar.component.spec.ts | 48 - .../password/password.component.spec.ts | 89 - .../register/register.component.spec.ts | 121 - .../settings/settings.component.spec.ts | 81 - .../app/admin/audits/audits.component.spec.ts | 133 - .../app/admin/audits/audits.service.spec.ts | 59 - .../configuration.component.spec.ts | 71 - .../configuration.service.spec.ts | 64 - .../app/admin/health/health.component.spec.ts | 321 - .../app/admin/logs/logs.component.spec.ts | 77 - .../spec/app/admin/logs/logs.service.spec.ts | 58 - .../admin/metrics/metrics.component.spec.ts | 55 - .../app/admin/metrics/metrics.service.spec.ts | 57 - ...management-delete-dialog.component.spec.ts | 54 - .../user-management-detail.component.spec.ts | 65 - .../user-management-update.component.spec.ts | 102 - .../user-management.component.spec.ts | 85 - .../app/core/user/account.service.spec.ts | 110 - .../spec/app/core/user/user.service.spec.ts | 66 - .../asset-delete-dialog.component.spec.ts | 52 - .../asset/asset-detail.component.spec.ts | 40 - .../asset/asset-update.component.spec.ts | 60 - .../entities/asset/asset.component.spec.ts | 128 - .../app/entities/asset/asset.service.spec.ts | 142 - .../customer-delete-dialog.component.spec.ts | 52 - .../customer-detail.component.spec.ts | 40 - .../customer-update.component.spec.ts | 60 - .../customer/customer.component.spec.ts | 128 - .../customer/customer.service.spec.ts | 172 - ...membership-delete-dialog.component.spec.ts | 52 - .../membership-detail.component.spec.ts | 40 - .../membership-update.component.spec.ts | 60 - .../membership/membership.component.spec.ts | 128 - .../membership/membership.service.spec.ts | 150 - ...pa-mandate-delete-dialog.component.spec.ts | 52 - .../sepa-mandate-detail.component.spec.ts | 40 - .../sepa-mandate-update.component.spec.ts | 60 - .../sepa-mandate.component.spec.ts | 128 - .../sepa-mandate/sepa-mandate.service.spec.ts | 174 - .../share-delete-dialog.component.spec.ts | 52 - .../share/share-detail.component.spec.ts | 40 - .../share/share-update.component.spec.ts | 60 - .../entities/share/share.component.spec.ts | 128 - .../app/entities/share/share.service.spec.ts | 142 - ...assignment-delete-dialog.component.spec.ts | 52 - ...r-role-assignment-detail.component.spec.ts | 40 - ...r-role-assignment-update.component.spec.ts | 60 - .../user-role-assignment.component.spec.ts | 128 - .../user-role-assignment.service.spec.ts | 108 - .../alert/alert-error.component.spec.ts | 135 - .../app/shared/login/login.component.spec.ts | 157 - .../app/shared/util/linebreaks-pipe.spec.ts | 37 - .../spec/app/shared/util/tablefilter.spec.ts | 145 - .../spec/helpers/mock-account.service.ts | 35 - .../spec/helpers/mock-active-modal.service.ts | 12 - .../spec/helpers/mock-alert.service.ts | 11 - .../helpers/mock-event-manager.service.ts | 12 - .../spec/helpers/mock-language.service.ts | 36 - .../spec/helpers/mock-login.service.ts | 29 - .../spec/helpers/mock-route.service.ts | 29 - .../helpers/mock-state-storage.service.ts | 22 - src/test/javascript/spec/helpers/spyobject.ts | 69 - src/test/javascript/spec/test.module.ts | 72 - src/test/resources/config/application.yml | 107 - .../resources/i18n/messages_en.properties | 1 - src/test/resources/logback.xml | 44 - .../resources/templates/mail/testEmail.html | 1 - tsconfig-aot.json | 28 - tsconfig.json | 25 - tslint.json | 76 - vue/.gitignore | 21 - vue/README.md | 37 - vue/babel.config.js | 5 - vue/package-lock.json | 11184 ---------- vue/package.json | 49 - vue/public/favicon.ico | Bin 1150 -> 0 bytes vue/public/index.html | 17 - vue/src/App.vue | 32 - vue/src/assets/logo.png | Bin 7426 -> 0 bytes vue/src/components/EntityList.vue | 25 - vue/src/components/HelloWorld.vue | 58 - vue/src/hsadmin.js | 28 - vue/src/main.js | 10 - vue/src/router.js | 45 - vue/src/views/About.vue | 5 - vue/src/views/Customer.vue | 55 - vue/src/views/Customers.vue | 29 - vue/src/views/Home.vue | 18 - vue/src/views/Login.vue | 37 - webpack/logo-jhipster.png | Bin 4459 -> 0 bytes webpack/utils.js | 30 - webpack/webpack.common.js | 96 - webpack/webpack.dev.js | 132 - webpack/webpack.prod.js | 125 - 747 files changed, 2225 insertions(+), 92268 deletions(-) delete mode 100644 .huskyrc delete mode 100644 .jhipster/Asset.json delete mode 100644 .jhipster/Customer.json delete mode 100644 .jhipster/Membership.json delete mode 100644 .jhipster/SepaMandate.json delete mode 100644 .jhipster/Share.json delete mode 100644 .jhipster/UserRoleAssignment.json delete mode 100644 .prettierignore delete mode 100644 .prettierrc delete mode 100644 .yo-rc.json create mode 100644 Glossary.md delete mode 100644 JHIPSTER.md delete mode 100644 Jenkinsfile create mode 100644 adr/2022-07-18.row-level-security-mechanism.md delete mode 100644 angular.json delete mode 100644 build-cucumber.gradle delete mode 100644 build-jacoco.gradle delete mode 100644 build-pitest.gradle delete mode 100644 build-spotless.gradle delete mode 100644 build.gradle delete mode 100644 cfg/spotless/eclipse_formatter.xml delete mode 100644 cfg/spotless/hsadminng.importorder delete mode 100755 cfg/spotless/javascript_formatters.xml delete mode 100644 gradle.properties delete mode 100644 gradle/docker.gradle delete mode 100644 gradle/profile_dev.gradle delete mode 100644 gradle/profile_prod.gradle delete mode 100644 gradle/sonar.gradle delete mode 100644 gradle/swagger.gradle delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100644 gradle/zipkin.gradle delete mode 100755 gradlew delete mode 100644 gradlew.bat delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 postcss.config.js delete mode 100644 proxy.conf.json delete mode 100644 settings.gradle create mode 100644 sql/00-util.sql create mode 100644 sql/10-rbac-base.sql create mode 100644 sql/11-rbac-view.sql create mode 100644 sql/19-rbac-tests.sql create mode 100644 sql/20-hs-base.sql create mode 100644 sql/21-hs-customer.sql create mode 100644 sql/22-hs-packages.sql create mode 100644 sql/23-hs-unixuser.sql create mode 100644 sql/24-hs-domain.sql create mode 100644 sql/25-hs-emailaddress.sql create mode 100644 sql/29-hs-statistics.sql create mode 100644 sql/examples.sql create mode 100644 sql/historization.sql delete mode 100644 sql/history-demo.sql create mode 100644 sql/rbac-statistics.sql create mode 100644 sql/rbac.md rename src/test/features/gitkeep => sql/rbac.pdf (100%) create mode 100644 sql/rbac.sql delete mode 100644 src/main/docker/.dockerignore delete mode 100644 src/main/docker/Dockerfile delete mode 100644 src/main/docker/app.yml delete mode 100644 src/main/docker/entrypoint.sh delete mode 100644 src/main/docker/jenkins.yml delete mode 100644 src/main/docker/postgresql.yml delete mode 100644 src/main/docker/sonar.yml delete mode 100644 src/main/docker/swagger-editor.yml delete mode 100644 src/main/java/org/hostsharing/hsadminng/ApplicationWebXml.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/HsadminNgApp.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/aop/logging/LoggingAspect.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/ApplicationProperties.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/AsyncConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/CacheConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/CloudDatabaseConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/Constants.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/DatabaseConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/DateTimeFormatConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/DefaultProfileUtil.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/JacksonConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/LiquibaseConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/LocaleConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/LoggingAspectConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/LoggingConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/SecurityConfiguration.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/WebConfigurer.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/audit/AuditEventConverter.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/audit/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/config/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/AbstractAuditingEntity.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/Asset.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/Authority.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/Customer.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/Membership.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/PersistentAuditEvent.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/SepaMandate.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/Share.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/User.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/UserRoleAssignment.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/enumeration/AssetAction.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/enumeration/CustomerKind.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/enumeration/ShareAction.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/enumeration/VatRegion.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/domain/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/liquibase/ReplaceCustomChange.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/AssetRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/AuthorityRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/CustomAuditEventRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/CustomerRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/MembershipRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/PersistenceAuditEventRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/SepaMandateRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/ShareRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/UserRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/UserRoleAssignmentRepository.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/repository/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/security/AuthoritiesConstants.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/security/DomainUserDetailsService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/security/SecurityUtils.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/security/SpringSecurityAuditorAware.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/security/UserNotActivatedException.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/security/jwt/JWTConfigurer.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/security/jwt/JWTFilter.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/security/jwt/TokenProvider.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/security/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/AssetQueryService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/AssetService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/AssetValidator.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/AuditEventService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/CustomerQueryService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/CustomerService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/IdToDtoResolver.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/MailService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/MembershipQueryService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/MembershipService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/MembershipValidator.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/SepaMandateQueryService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/SepaMandateService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/ShareQueryService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/ShareService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/ShareValidator.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/UserRoleAssignmentQueryService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/UserRoleAssignmentService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/UserService.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessFor.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessMappings.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/EntityTypeId.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilter.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonFieldReader.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonFieldWriter.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonDeserializerWithAccessFilter.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonSerializerWithAccessFilter.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/ParentId.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/Role.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/SelfId.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/AssetCriteria.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/AssetDTO.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/CustomerCriteria.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/CustomerDTO.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/FluentBuilder.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/MembershipCriteria.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/MembershipDTO.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/PasswordChangeDTO.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/SepaMandateCriteria.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTO.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/ShareCriteria.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/ShareDTO.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/UserDTO.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/UserRoleAssignmentCriteria.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/dto/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/mapper/AssetMapper.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/mapper/CustomerMapper.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/mapper/EntityMapper.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/mapper/MembershipMapper.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/mapper/SepaMandateMapper.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/mapper/ShareMapper.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/mapper/UserMapper.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/mapper/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/util/RandomUtil.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/service/util/ReflectionUtil.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/AccountResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/AssetResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/AuditResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/CustomerResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/LogsResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/MembershipResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/SepaMandateResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/ShareResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/UserJWTController.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/UserResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResource.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/BadRequestAlertException.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/CustomParameterizedException.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/EmailAlreadyUsedException.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/EmailNotFoundException.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/ErrorConstants.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslator.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/FieldErrorVM.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/InternalServerErrorException.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/InvalidPasswordException.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/LoginAlreadyUsedException.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/errors/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/package-info.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/util/HeaderUtil.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/util/PaginationUtil.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/vm/KeyAndPasswordVM.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/vm/LoggerVM.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/vm/LoginVM.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/vm/ManagedUserVM.java delete mode 100644 src/main/java/org/hostsharing/hsadminng/web/rest/vm/package-info.java delete mode 100644 src/main/jdl/accessrights.jdl delete mode 100644 src/main/jdl/customer.jdl delete mode 100644 src/main/jib/entrypoint.sh delete mode 100644 src/main/resources/.h2.server.properties delete mode 100644 src/main/resources/banner.txt delete mode 100644 src/main/resources/config/application-dev.yml delete mode 100644 src/main/resources/config/application-h2file.yml delete mode 100644 src/main/resources/config/application-h2mem.yml delete mode 100644 src/main/resources/config/application-pgsql.yml delete mode 100644 src/main/resources/config/application-prod.yml delete mode 100644 src/main/resources/config/application-tls.yml delete mode 100644 src/main/resources/config/application.yml delete mode 100644 src/main/resources/config/liquibase/authorities.csv delete mode 100644 src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105332_added_entity_Customer.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105333_added_entity_Membership.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105333_added_entity_constraints_Membership.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105334_added_entity_Share.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105334_added_entity_constraints_Share.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105335_added_entity_Asset.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105335_added_entity_constraints_Asset.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105336_added_entity_SepaMandate.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105336_added_entity_constraints_SepaMandate.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105342_added_entity_UserRoleAssignment.xml delete mode 100644 src/main/resources/config/liquibase/changelog/20190507105342_added_entity_constraints_UserRoleAssignment.xml delete mode 100644 src/main/resources/config/liquibase/changelog/constraints_Membership.xml delete mode 100644 src/main/resources/config/liquibase/changelog/constraints_UserRoleAssignment.xml delete mode 100644 src/main/resources/config/liquibase/historicization/historicization.xml delete mode 100644 src/main/resources/config/liquibase/historicization/historicization_Asset.xml delete mode 100644 src/main/resources/config/liquibase/historicization/historicization_Customer.xml delete mode 100644 src/main/resources/config/liquibase/historicization/historicization_Membership.xml delete mode 100644 src/main/resources/config/liquibase/historicization/historicization_SepaMandate.xml delete mode 100644 src/main/resources/config/liquibase/historicization/historicization_Share.xml delete mode 100644 src/main/resources/config/liquibase/historicization/historicization_User.xml delete mode 100644 src/main/resources/config/liquibase/historicization/historicization_UserAuthority.xml delete mode 100644 src/main/resources/config/liquibase/historicization/historicization_UserRoleAssignment.xml delete mode 100644 src/main/resources/config/liquibase/master.xml delete mode 100644 src/main/resources/config/liquibase/sample-data/assets.csv delete mode 100644 src/main/resources/config/liquibase/sample-data/assets.xml delete mode 100644 src/main/resources/config/liquibase/sample-data/customers.csv delete mode 100644 src/main/resources/config/liquibase/sample-data/customers.xml delete mode 100644 src/main/resources/config/liquibase/sample-data/memberships.csv delete mode 100644 src/main/resources/config/liquibase/sample-data/memberships.xml delete mode 100644 src/main/resources/config/liquibase/sample-data/sepamandates.csv delete mode 100644 src/main/resources/config/liquibase/sample-data/sepamandates.xml delete mode 100644 src/main/resources/config/liquibase/sample-data/shares.csv delete mode 100644 src/main/resources/config/liquibase/sample-data/shares.xml delete mode 100644 src/main/resources/config/liquibase/sample-data/user_role_assignments.csv delete mode 100644 src/main/resources/config/liquibase/sample-data/user_role_assignments.xml delete mode 100644 src/main/resources/config/liquibase/sample-data/users.csv delete mode 100644 src/main/resources/config/liquibase/sample-data/users.xml delete mode 100644 src/main/resources/config/liquibase/sample-data/users_authorities.csv delete mode 100644 src/main/resources/config/liquibase/sample-data/users_authorities.xml delete mode 100644 src/main/resources/config/liquibase/users.csv delete mode 100644 src/main/resources/config/liquibase/users_authorities.csv delete mode 100644 src/main/resources/config/tls/keystore.p12 delete mode 100644 src/main/resources/i18n/messages.properties delete mode 100644 src/main/resources/i18n/messages_de.properties delete mode 100644 src/main/resources/i18n/messages_en.properties delete mode 100644 src/main/resources/idea.gdsl delete mode 100644 src/main/resources/logback-spring.xml delete mode 100644 src/main/resources/swagger/api.yml delete mode 100644 src/main/resources/templates/error.html delete mode 100644 src/main/resources/templates/mail/activationEmail.html delete mode 100644 src/main/resources/templates/mail/creationEmail.html delete mode 100644 src/main/resources/templates/mail/passwordResetEmail.html delete mode 100644 src/main/webapp/404.html delete mode 100644 src/main/webapp/app/account/account.module.ts delete mode 100644 src/main/webapp/app/account/account.route.ts delete mode 100644 src/main/webapp/app/account/activate/activate.component.html delete mode 100644 src/main/webapp/app/account/activate/activate.component.ts delete mode 100644 src/main/webapp/app/account/activate/activate.route.ts delete mode 100644 src/main/webapp/app/account/activate/activate.service.ts delete mode 100644 src/main/webapp/app/account/index.ts delete mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html delete mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts delete mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts delete mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts delete mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.component.html delete mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts delete mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts delete mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts delete mode 100644 src/main/webapp/app/account/password/password-strength-bar.component.ts delete mode 100644 src/main/webapp/app/account/password/password-strength-bar.css delete mode 100644 src/main/webapp/app/account/password/password.component.html delete mode 100644 src/main/webapp/app/account/password/password.component.ts delete mode 100644 src/main/webapp/app/account/password/password.route.ts delete mode 100644 src/main/webapp/app/account/password/password.service.ts delete mode 100644 src/main/webapp/app/account/register/register.component.html delete mode 100644 src/main/webapp/app/account/register/register.component.ts delete mode 100644 src/main/webapp/app/account/register/register.route.ts delete mode 100644 src/main/webapp/app/account/register/register.service.ts delete mode 100644 src/main/webapp/app/account/settings/settings.component.html delete mode 100644 src/main/webapp/app/account/settings/settings.component.ts delete mode 100644 src/main/webapp/app/account/settings/settings.route.ts delete mode 100644 src/main/webapp/app/admin/admin.module.ts delete mode 100644 src/main/webapp/app/admin/admin.route.ts delete mode 100644 src/main/webapp/app/admin/audits/audit-data.model.ts delete mode 100644 src/main/webapp/app/admin/audits/audit.model.ts delete mode 100644 src/main/webapp/app/admin/audits/audits.component.html delete mode 100644 src/main/webapp/app/admin/audits/audits.component.ts delete mode 100644 src/main/webapp/app/admin/audits/audits.route.ts delete mode 100644 src/main/webapp/app/admin/audits/audits.service.ts delete mode 100644 src/main/webapp/app/admin/configuration/configuration.component.html delete mode 100644 src/main/webapp/app/admin/configuration/configuration.component.ts delete mode 100644 src/main/webapp/app/admin/configuration/configuration.route.ts delete mode 100644 src/main/webapp/app/admin/configuration/configuration.service.ts delete mode 100644 src/main/webapp/app/admin/docs/docs.component.html delete mode 100644 src/main/webapp/app/admin/docs/docs.component.ts delete mode 100644 src/main/webapp/app/admin/docs/docs.route.ts delete mode 100644 src/main/webapp/app/admin/health/health-modal.component.html delete mode 100644 src/main/webapp/app/admin/health/health-modal.component.ts delete mode 100644 src/main/webapp/app/admin/health/health.component.html delete mode 100644 src/main/webapp/app/admin/health/health.component.ts delete mode 100644 src/main/webapp/app/admin/health/health.route.ts delete mode 100644 src/main/webapp/app/admin/health/health.service.ts delete mode 100644 src/main/webapp/app/admin/index.ts delete mode 100644 src/main/webapp/app/admin/logs/log.model.ts delete mode 100644 src/main/webapp/app/admin/logs/logs.component.html delete mode 100644 src/main/webapp/app/admin/logs/logs.component.ts delete mode 100644 src/main/webapp/app/admin/logs/logs.route.ts delete mode 100644 src/main/webapp/app/admin/logs/logs.service.ts delete mode 100644 src/main/webapp/app/admin/metrics/metrics.component.html delete mode 100644 src/main/webapp/app/admin/metrics/metrics.component.ts delete mode 100644 src/main/webapp/app/admin/metrics/metrics.route.ts delete mode 100644 src/main/webapp/app/admin/metrics/metrics.service.ts delete mode 100644 src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html delete mode 100644 src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts delete mode 100644 src/main/webapp/app/admin/user-management/user-management-detail.component.html delete mode 100644 src/main/webapp/app/admin/user-management/user-management-detail.component.ts delete mode 100644 src/main/webapp/app/admin/user-management/user-management-update.component.html delete mode 100644 src/main/webapp/app/admin/user-management/user-management-update.component.ts delete mode 100644 src/main/webapp/app/admin/user-management/user-management.component.html delete mode 100644 src/main/webapp/app/admin/user-management/user-management.component.ts delete mode 100644 src/main/webapp/app/admin/user-management/user-management.route.ts delete mode 100644 src/main/webapp/app/app-routing.module.ts delete mode 100644 src/main/webapp/app/app.constants.ts delete mode 100644 src/main/webapp/app/app.main.ts delete mode 100644 src/main/webapp/app/app.module.ts delete mode 100644 src/main/webapp/app/blocks/config/prod.config.ts delete mode 100644 src/main/webapp/app/blocks/config/uib-pagination.config.ts delete mode 100644 src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts delete mode 100644 src/main/webapp/app/blocks/interceptor/auth.interceptor.ts delete mode 100644 src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts delete mode 100644 src/main/webapp/app/blocks/interceptor/notification.interceptor.ts delete mode 100644 src/main/webapp/app/core/auth/account.service.ts delete mode 100644 src/main/webapp/app/core/auth/auth-jwt.service.ts delete mode 100644 src/main/webapp/app/core/auth/csrf.service.ts delete mode 100644 src/main/webapp/app/core/auth/state-storage.service.ts delete mode 100644 src/main/webapp/app/core/auth/user-route-access-service.ts delete mode 100644 src/main/webapp/app/core/core.module.ts delete mode 100644 src/main/webapp/app/core/index.ts delete mode 100644 src/main/webapp/app/core/language/language.constants.ts delete mode 100644 src/main/webapp/app/core/language/language.helper.ts delete mode 100644 src/main/webapp/app/core/login/login-modal.service.ts delete mode 100644 src/main/webapp/app/core/login/login.service.ts delete mode 100644 src/main/webapp/app/core/user/account.model.ts delete mode 100644 src/main/webapp/app/core/user/user.model.ts delete mode 100644 src/main/webapp/app/core/user/user.service.ts delete mode 100644 src/main/webapp/app/entities/asset/asset-delete-dialog.component.html delete mode 100644 src/main/webapp/app/entities/asset/asset-delete-dialog.component.ts delete mode 100644 src/main/webapp/app/entities/asset/asset-detail.component.html delete mode 100644 src/main/webapp/app/entities/asset/asset-detail.component.ts delete mode 100644 src/main/webapp/app/entities/asset/asset-update.component.html delete mode 100644 src/main/webapp/app/entities/asset/asset-update.component.ts delete mode 100644 src/main/webapp/app/entities/asset/asset.component.html delete mode 100644 src/main/webapp/app/entities/asset/asset.component.ts delete mode 100644 src/main/webapp/app/entities/asset/asset.module.ts delete mode 100644 src/main/webapp/app/entities/asset/asset.route.ts delete mode 100644 src/main/webapp/app/entities/asset/asset.service.ts delete mode 100644 src/main/webapp/app/entities/asset/index.ts delete mode 100644 src/main/webapp/app/entities/customer/customer-delete-dialog.component.html delete mode 100644 src/main/webapp/app/entities/customer/customer-delete-dialog.component.ts delete mode 100644 src/main/webapp/app/entities/customer/customer-detail.component.html delete mode 100644 src/main/webapp/app/entities/customer/customer-detail.component.ts delete mode 100644 src/main/webapp/app/entities/customer/customer-update.component.html delete mode 100644 src/main/webapp/app/entities/customer/customer-update.component.ts delete mode 100644 src/main/webapp/app/entities/customer/customer.component.html delete mode 100644 src/main/webapp/app/entities/customer/customer.component.ts delete mode 100644 src/main/webapp/app/entities/customer/customer.module.ts delete mode 100644 src/main/webapp/app/entities/customer/customer.route.ts delete mode 100644 src/main/webapp/app/entities/customer/customer.service.ts delete mode 100644 src/main/webapp/app/entities/customer/index.ts delete mode 100644 src/main/webapp/app/entities/entity.module.ts delete mode 100644 src/main/webapp/app/entities/membership/index.ts delete mode 100644 src/main/webapp/app/entities/membership/membership-delete-dialog.component.html delete mode 100644 src/main/webapp/app/entities/membership/membership-delete-dialog.component.ts delete mode 100644 src/main/webapp/app/entities/membership/membership-detail.component.html delete mode 100644 src/main/webapp/app/entities/membership/membership-detail.component.ts delete mode 100644 src/main/webapp/app/entities/membership/membership-update.component.html delete mode 100644 src/main/webapp/app/entities/membership/membership-update.component.ts delete mode 100644 src/main/webapp/app/entities/membership/membership.component.html delete mode 100644 src/main/webapp/app/entities/membership/membership.component.ts delete mode 100644 src/main/webapp/app/entities/membership/membership.module.ts delete mode 100644 src/main/webapp/app/entities/membership/membership.route.ts delete mode 100644 src/main/webapp/app/entities/membership/membership.service.ts delete mode 100644 src/main/webapp/app/entities/sepa-mandate/index.ts delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.html delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.ts delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate-detail.component.html delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate-detail.component.ts delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate-update.component.html delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate-update.component.ts delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate.component.html delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate.component.ts delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate.module.ts delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate.route.ts delete mode 100644 src/main/webapp/app/entities/sepa-mandate/sepa-mandate.service.ts delete mode 100644 src/main/webapp/app/entities/share/index.ts delete mode 100644 src/main/webapp/app/entities/share/share-delete-dialog.component.html delete mode 100644 src/main/webapp/app/entities/share/share-delete-dialog.component.ts delete mode 100644 src/main/webapp/app/entities/share/share-detail.component.html delete mode 100644 src/main/webapp/app/entities/share/share-detail.component.ts delete mode 100644 src/main/webapp/app/entities/share/share-update.component.html delete mode 100644 src/main/webapp/app/entities/share/share-update.component.ts delete mode 100644 src/main/webapp/app/entities/share/share.component.html delete mode 100644 src/main/webapp/app/entities/share/share.component.ts delete mode 100644 src/main/webapp/app/entities/share/share.module.ts delete mode 100644 src/main/webapp/app/entities/share/share.route.ts delete mode 100644 src/main/webapp/app/entities/share/share.service.ts delete mode 100644 src/main/webapp/app/entities/user-role-assignment/index.ts delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.html delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.ts delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment-detail.component.html delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment-detail.component.ts delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment-update.component.html delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment-update.component.ts delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment.component.html delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment.component.ts delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment.module.ts delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment.route.ts delete mode 100644 src/main/webapp/app/entities/user-role-assignment/user-role-assignment.service.ts delete mode 100644 src/main/webapp/app/home/home.component.html delete mode 100644 src/main/webapp/app/home/home.component.ts delete mode 100644 src/main/webapp/app/home/home.css delete mode 100644 src/main/webapp/app/home/home.module.ts delete mode 100644 src/main/webapp/app/home/home.route.ts delete mode 100644 src/main/webapp/app/home/index.ts delete mode 100644 src/main/webapp/app/layouts/error/error.component.html delete mode 100644 src/main/webapp/app/layouts/error/error.component.ts delete mode 100644 src/main/webapp/app/layouts/error/error.route.ts delete mode 100644 src/main/webapp/app/layouts/footer/footer.component.html delete mode 100644 src/main/webapp/app/layouts/footer/footer.component.ts delete mode 100644 src/main/webapp/app/layouts/index.ts delete mode 100644 src/main/webapp/app/layouts/main/main.component.html delete mode 100644 src/main/webapp/app/layouts/main/main.component.ts delete mode 100644 src/main/webapp/app/layouts/navbar/active-menu.directive.ts delete mode 100644 src/main/webapp/app/layouts/navbar/navbar.component.html delete mode 100644 src/main/webapp/app/layouts/navbar/navbar.component.ts delete mode 100644 src/main/webapp/app/layouts/navbar/navbar.css delete mode 100644 src/main/webapp/app/layouts/navbar/navbar.route.ts delete mode 100644 src/main/webapp/app/layouts/profiles/page-ribbon.component.ts delete mode 100644 src/main/webapp/app/layouts/profiles/page-ribbon.css delete mode 100644 src/main/webapp/app/layouts/profiles/profile-info.model.ts delete mode 100644 src/main/webapp/app/layouts/profiles/profile.service.ts delete mode 100644 src/main/webapp/app/polyfills.ts delete mode 100644 src/main/webapp/app/shared/alert/alert-error.component.ts delete mode 100644 src/main/webapp/app/shared/alert/alert.component.ts delete mode 100644 src/main/webapp/app/shared/auth/has-any-authority.directive.ts delete mode 100644 src/main/webapp/app/shared/constants/error.constants.ts delete mode 100644 src/main/webapp/app/shared/constants/input.constants.ts delete mode 100644 src/main/webapp/app/shared/constants/pagination.constants.ts delete mode 100644 src/main/webapp/app/shared/index.ts delete mode 100644 src/main/webapp/app/shared/language/find-language-from-key.pipe.ts delete mode 100644 src/main/webapp/app/shared/login/login.component.html delete mode 100644 src/main/webapp/app/shared/login/login.component.ts delete mode 100644 src/main/webapp/app/shared/model/asset.model.ts delete mode 100644 src/main/webapp/app/shared/model/customer.model.ts delete mode 100644 src/main/webapp/app/shared/model/membership.model.ts delete mode 100644 src/main/webapp/app/shared/model/sepa-mandate.model.ts delete mode 100644 src/main/webapp/app/shared/model/share.model.ts delete mode 100644 src/main/webapp/app/shared/model/user-role-assignment.model.ts delete mode 100644 src/main/webapp/app/shared/shared-common.module.ts delete mode 100644 src/main/webapp/app/shared/shared-libs.module.ts delete mode 100644 src/main/webapp/app/shared/shared.module.ts delete mode 100644 src/main/webapp/app/shared/util/datepicker-adapter.ts delete mode 100644 src/main/webapp/app/shared/util/linebreaks-pipe.ts delete mode 100644 src/main/webapp/app/shared/util/request-util.ts delete mode 100644 src/main/webapp/app/shared/util/tablefilter.ts delete mode 100644 src/main/webapp/app/vendor.ts delete mode 100644 src/main/webapp/content/css/documentation.css delete mode 100644 src/main/webapp/content/css/global.css delete mode 100644 src/main/webapp/content/css/loading.css delete mode 100644 src/main/webapp/content/css/vendor.css delete mode 100755 src/main/webapp/content/images/jhipster_family_member_0.svg delete mode 100644 src/main/webapp/content/images/jhipster_family_member_0_head-192.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_0_head-256.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_0_head-384.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_0_head-512.png delete mode 100755 src/main/webapp/content/images/jhipster_family_member_1.svg delete mode 100644 src/main/webapp/content/images/jhipster_family_member_1_head-192.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_1_head-256.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_1_head-384.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_1_head-512.png delete mode 100755 src/main/webapp/content/images/jhipster_family_member_2.svg delete mode 100644 src/main/webapp/content/images/jhipster_family_member_2_head-192.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_2_head-256.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_2_head-384.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_2_head-512.png delete mode 100755 src/main/webapp/content/images/jhipster_family_member_3.svg delete mode 100644 src/main/webapp/content/images/jhipster_family_member_3_head-192.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_3_head-256.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_3_head-384.png delete mode 100644 src/main/webapp/content/images/jhipster_family_member_3_head-512.png delete mode 100755 src/main/webapp/content/images/logo-jhipster.png delete mode 100755 src/main/webapp/favicon.ico delete mode 100644 src/main/webapp/i18n/de/activate.json delete mode 100644 src/main/webapp/i18n/de/asset.json delete mode 100644 src/main/webapp/i18n/de/assetAction.json delete mode 100644 src/main/webapp/i18n/de/audits.json delete mode 100644 src/main/webapp/i18n/de/configuration.json delete mode 100644 src/main/webapp/i18n/de/custom-error.json delete mode 100644 src/main/webapp/i18n/de/customer.json delete mode 100644 src/main/webapp/i18n/de/customerKind.json delete mode 100644 src/main/webapp/i18n/de/error.json delete mode 100644 src/main/webapp/i18n/de/global.json delete mode 100644 src/main/webapp/i18n/de/health.json delete mode 100644 src/main/webapp/i18n/de/home.json delete mode 100644 src/main/webapp/i18n/de/login.json delete mode 100644 src/main/webapp/i18n/de/logs.json delete mode 100644 src/main/webapp/i18n/de/membership.json delete mode 100644 src/main/webapp/i18n/de/metrics.json delete mode 100644 src/main/webapp/i18n/de/password.json delete mode 100644 src/main/webapp/i18n/de/register.json delete mode 100644 src/main/webapp/i18n/de/reset.json delete mode 100644 src/main/webapp/i18n/de/sepaMandate.json delete mode 100644 src/main/webapp/i18n/de/sessions.json delete mode 100644 src/main/webapp/i18n/de/settings.json delete mode 100644 src/main/webapp/i18n/de/share.json delete mode 100644 src/main/webapp/i18n/de/shareAction.json delete mode 100644 src/main/webapp/i18n/de/user-management.json delete mode 100644 src/main/webapp/i18n/de/userRole.json delete mode 100644 src/main/webapp/i18n/de/userRoleAssignment.json delete mode 100644 src/main/webapp/i18n/de/vatRegion.json delete mode 100644 src/main/webapp/i18n/en/activate.json delete mode 100644 src/main/webapp/i18n/en/asset.json delete mode 100644 src/main/webapp/i18n/en/assetAction.json delete mode 100644 src/main/webapp/i18n/en/audits.json delete mode 100644 src/main/webapp/i18n/en/configuration.json delete mode 100644 src/main/webapp/i18n/en/custom-error.json delete mode 100644 src/main/webapp/i18n/en/customer.json delete mode 100644 src/main/webapp/i18n/en/customerKind.json delete mode 100644 src/main/webapp/i18n/en/error.json delete mode 100644 src/main/webapp/i18n/en/global.json delete mode 100644 src/main/webapp/i18n/en/health.json delete mode 100644 src/main/webapp/i18n/en/home.json delete mode 100644 src/main/webapp/i18n/en/login.json delete mode 100644 src/main/webapp/i18n/en/logs.json delete mode 100644 src/main/webapp/i18n/en/membership.json delete mode 100644 src/main/webapp/i18n/en/metrics.json delete mode 100644 src/main/webapp/i18n/en/password.json delete mode 100644 src/main/webapp/i18n/en/register.json delete mode 100644 src/main/webapp/i18n/en/reset.json delete mode 100644 src/main/webapp/i18n/en/sepaMandate.json delete mode 100644 src/main/webapp/i18n/en/sessions.json delete mode 100644 src/main/webapp/i18n/en/settings.json delete mode 100644 src/main/webapp/i18n/en/share.json delete mode 100644 src/main/webapp/i18n/en/shareAction.json delete mode 100644 src/main/webapp/i18n/en/user-management.json delete mode 100644 src/main/webapp/i18n/en/userRole.json delete mode 100644 src/main/webapp/i18n/en/userRoleAssignment.json delete mode 100644 src/main/webapp/i18n/en/vatRegion.json delete mode 100644 src/main/webapp/index.html delete mode 100644 src/main/webapp/manifest.webapp delete mode 100644 src/main/webapp/robots.txt delete mode 100644 src/main/webapp/swagger-ui/dist/images/throbber.gif delete mode 100644 src/main/webapp/swagger-ui/index.html delete mode 100644 src/test/features/user/user.feature delete mode 100644 src/test/java/org/hostsharing/hsadminng/config/WebConfigurerTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/config/WebConfigurerTestController.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/config/timezone/HibernateTimeZoneTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/cucumber/CucumberContextConfiguration.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/cucumber/CucumberTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/cucumber/stepdefs/StepDefs.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/cucumber/stepdefs/UserStepDefs.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/liquibase/ReplaceCustomChangeUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/repository/AssetRepositoryIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/repository/CustomAuditEventRepositoryIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/repository/CustomerRepositoryIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/repository/MembershipRepositoryIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/repository/SepaMandateRepositoryIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/repository/ShareRepositoryIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/repository/timezone/DateTimeWrapper.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/repository/timezone/DateTimeWrapperRepository.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/security/DomainUserDetailsServiceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/security/SecurityUtilsUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/security/jwt/JWTFilterTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/security/jwt/TokenProviderTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/AssetServiceUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/AssetValidatorUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/MailServiceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/MembershipServiceUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/MembershipValidatorUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/ShareServiceUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/ShareValidatorUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/UserRoleAssignmentServiceUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/UserServiceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilterTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilterTestFixture.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonBuilder.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilterUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilterUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/accessfilter/RoleUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextDouble.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextFake.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextMock.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/AccessMappingsUnitTestBase.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/AssetDTOIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/AssetDTOUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/CustomerDTOUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTOIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTOUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/dto/UserRoleAssignmentUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/mapper/UserMapperTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/service/util/ReflectionUtilUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/AccountResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/AssetResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/AuditResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/CustomerResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/LogsResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/MembershipResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/MembershipResourceUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/SepaMandateResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/SepaMandateResourceUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/ShareResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/TestUtil.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/UserJWTControllerIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/UserResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResourceIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResourceUnitTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslatorIntTest.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslatorTestController.java delete mode 100644 src/test/java/org/hostsharing/hsadminng/web/rest/util/PaginationUtilUnitTest.java delete mode 100644 src/test/javascript/jest-global-mocks.ts delete mode 100644 src/test/javascript/jest.conf.js delete mode 100644 src/test/javascript/jest.ts delete mode 100644 src/test/javascript/spec/app/account/activate/activate.component.spec.ts delete mode 100644 src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts delete mode 100644 src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts delete mode 100644 src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts delete mode 100644 src/test/javascript/spec/app/account/password/password.component.spec.ts delete mode 100644 src/test/javascript/spec/app/account/register/register.component.spec.ts delete mode 100644 src/test/javascript/spec/app/account/settings/settings.component.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/audits/audits.component.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/audits/audits.service.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/configuration/configuration.component.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/configuration/configuration.service.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/health/health.component.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/logs/logs.component.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/logs/logs.service.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/metrics/metrics.component.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/metrics/metrics.service.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/user-management/user-management-delete-dialog.component.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/user-management/user-management-detail.component.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/user-management/user-management-update.component.spec.ts delete mode 100644 src/test/javascript/spec/app/admin/user-management/user-management.component.spec.ts delete mode 100644 src/test/javascript/spec/app/core/user/account.service.spec.ts delete mode 100644 src/test/javascript/spec/app/core/user/user.service.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/asset/asset-delete-dialog.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/asset/asset-detail.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/asset/asset-update.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/asset/asset.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/asset/asset.service.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/customer/customer-delete-dialog.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/customer/customer-detail.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/customer/customer-update.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/customer/customer.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/customer/customer.service.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/membership/membership-delete-dialog.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/membership/membership-detail.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/membership/membership-update.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/membership/membership.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/membership/membership.service.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-detail.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-update.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate.service.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/share/share-delete-dialog.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/share/share-detail.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/share/share-update.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/share/share.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/share/share.service.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-detail.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-update.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment.component.spec.ts delete mode 100644 src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment.service.spec.ts delete mode 100644 src/test/javascript/spec/app/shared/alert/alert-error.component.spec.ts delete mode 100644 src/test/javascript/spec/app/shared/login/login.component.spec.ts delete mode 100644 src/test/javascript/spec/app/shared/util/linebreaks-pipe.spec.ts delete mode 100644 src/test/javascript/spec/app/shared/util/tablefilter.spec.ts delete mode 100644 src/test/javascript/spec/helpers/mock-account.service.ts delete mode 100644 src/test/javascript/spec/helpers/mock-active-modal.service.ts delete mode 100644 src/test/javascript/spec/helpers/mock-alert.service.ts delete mode 100644 src/test/javascript/spec/helpers/mock-event-manager.service.ts delete mode 100644 src/test/javascript/spec/helpers/mock-language.service.ts delete mode 100644 src/test/javascript/spec/helpers/mock-login.service.ts delete mode 100644 src/test/javascript/spec/helpers/mock-route.service.ts delete mode 100644 src/test/javascript/spec/helpers/mock-state-storage.service.ts delete mode 100644 src/test/javascript/spec/helpers/spyobject.ts delete mode 100644 src/test/javascript/spec/test.module.ts delete mode 100644 src/test/resources/config/application.yml delete mode 100644 src/test/resources/i18n/messages_en.properties delete mode 100644 src/test/resources/logback.xml delete mode 100644 src/test/resources/templates/mail/testEmail.html delete mode 100644 tsconfig-aot.json delete mode 100644 tsconfig.json delete mode 100644 tslint.json delete mode 100644 vue/.gitignore delete mode 100644 vue/README.md delete mode 100644 vue/babel.config.js delete mode 100644 vue/package-lock.json delete mode 100644 vue/package.json delete mode 100644 vue/public/favicon.ico delete mode 100644 vue/public/index.html delete mode 100644 vue/src/App.vue delete mode 100644 vue/src/assets/logo.png delete mode 100644 vue/src/components/EntityList.vue delete mode 100644 vue/src/components/HelloWorld.vue delete mode 100644 vue/src/hsadmin.js delete mode 100644 vue/src/main.js delete mode 100644 vue/src/router.js delete mode 100644 vue/src/views/About.vue delete mode 100644 vue/src/views/Customer.vue delete mode 100644 vue/src/views/Customers.vue delete mode 100644 vue/src/views/Home.vue delete mode 100644 vue/src/views/Login.vue delete mode 100644 webpack/logo-jhipster.png delete mode 100644 webpack/utils.js delete mode 100644 webpack/webpack.common.js delete mode 100644 webpack/webpack.dev.js delete mode 100644 webpack/webpack.prod.js diff --git a/.editorconfig b/.editorconfig index a79c052f..51ceaea8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,7 +18,3 @@ insert_final_newline = true [*.md] trim_trailing_whitespace = false - -[package.json] -indent_style = space -indent_size = 2 diff --git a/.huskyrc b/.huskyrc deleted file mode 100644 index 9e18d876..00000000 --- a/.huskyrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "hooks": { - "pre-commit": "lint-staged" - } -} diff --git a/.jhipster/Asset.json b/.jhipster/Asset.json deleted file mode 100644 index ff8816aa..00000000 --- a/.jhipster/Asset.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "Asset", - "fields": [ - { - "fieldName": "documentDate", - "fieldType": "LocalDate", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "valueDate", - "fieldType": "LocalDate", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "action", - "fieldType": "AssetAction", - "fieldValues": "PAYMENT,HANDOVER,ADOPTION,LOSS,CLEARING,PAYBACK", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "amount", - "fieldType": "BigDecimal", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "remark", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 160 - } - ], - "relationships": [ - { - "relationshipType": "many-to-one", - "otherEntityName": "membership", - "otherEntityRelationshipName": "asset", - "relationshipValidateRules": "required", - "relationshipName": "membership", - "otherEntityField": "admissionDocumentDate" - } - ], - "changelogDate": "20190507105335", - "entityTableName": "asset", - "dto": "mapstruct", - "pagination": "infinite-scroll", - "service": "serviceClass", - "jpaMetamodelFiltering": true, - "fluentMethods": true, - "clientRootFolder": "", - "applications": "*" -} \ No newline at end of file diff --git a/.jhipster/Customer.json b/.jhipster/Customer.json deleted file mode 100644 index 97a58c7e..00000000 --- a/.jhipster/Customer.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "name": "Customer", - "fields": [ - { - "fieldName": "reference", - "fieldType": "Integer", - "fieldValidateRules": [ - "required", - "unique", - "min", - "max" - ], - "fieldValidateRulesMin": 10000, - "fieldValidateRulesMax": 99999 - }, - { - "fieldName": "prefix", - "fieldType": "String", - "fieldValidateRules": [ - "required", - "maxlength", - "unique", - "pattern" - ], - "fieldValidateRulesMaxlength": 3, - "fieldValidateRulesPattern": "[a-z][a-z0-9]+" - }, - { - "fieldName": "name", - "fieldType": "String", - "fieldValidateRules": [ - "required", - "maxlength" - ], - "fieldValidateRulesMaxlength": 80 - }, - { - "fieldName": "kind", - "fieldType": "CustomerKind", - "fieldValues": "NATURAL,LEGAL", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "birthDate", - "fieldType": "LocalDate" - }, - { - "fieldName": "birthPlace", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 80 - }, - { - "fieldName": "registrationCourt", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 80 - }, - { - "fieldName": "registrationNumber", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 80 - }, - { - "fieldName": "vatRegion", - "fieldType": "VatRegion", - "fieldValues": "DOMESTIC,EU,OTHER", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "vatNumber", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 40 - }, - { - "fieldName": "contractualSalutation", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 80 - }, - { - "fieldName": "contractualAddress", - "fieldType": "String", - "fieldValidateRules": [ - "required", - "maxlength" - ], - "fieldValidateRulesMaxlength": 400 - }, - { - "fieldName": "billingSalutation", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 80 - }, - { - "fieldName": "billingAddress", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 400 - }, - { - "fieldName": "remark", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 160 - } - ], - "relationships": [ - { - "relationshipType": "one-to-many", - "otherEntityName": "membership", - "otherEntityRelationshipName": "customer", - "relationshipName": "membership" - }, - { - "relationshipType": "one-to-many", - "otherEntityName": "sepaMandate", - "otherEntityRelationshipName": "customer", - "relationshipName": "sepamandate" - } - ], - "changelogDate": "20190507105332", - "entityTableName": "customer", - "dto": "mapstruct", - "pagination": "infinite-scroll", - "service": "serviceClass", - "jpaMetamodelFiltering": true, - "fluentMethods": true, - "clientRootFolder": "", - "applications": "*" -} \ No newline at end of file diff --git a/.jhipster/Membership.json b/.jhipster/Membership.json deleted file mode 100644 index 90797b5b..00000000 --- a/.jhipster/Membership.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "Membership", - "fields": [ - { - "fieldName": "admissionDocumentDate", - "fieldType": "LocalDate", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "cancellationDocumentDate", - "fieldType": "LocalDate" - }, - { - "fieldName": "memberFromDate", - "fieldType": "LocalDate", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "memberUntilDate", - "fieldType": "LocalDate" - }, - { - "fieldName": "remark", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 160 - } - ], - "relationships": [ - { - "relationshipType": "one-to-many", - "otherEntityName": "share", - "otherEntityRelationshipName": "membership", - "relationshipName": "share" - }, - { - "relationshipType": "one-to-many", - "otherEntityName": "asset", - "otherEntityRelationshipName": "membership", - "relationshipName": "asset" - }, - { - "relationshipType": "many-to-one", - "otherEntityName": "customer", - "otherEntityRelationshipName": "membership", - "relationshipValidateRules": "required", - "relationshipName": "customer", - "otherEntityField": "prefix" - } - ], - "changelogDate": "20190507105333", - "entityTableName": "membership", - "dto": "mapstruct", - "pagination": "infinite-scroll", - "service": "serviceClass", - "jpaMetamodelFiltering": true, - "fluentMethods": true, - "clientRootFolder": "", - "applications": "*" -} \ No newline at end of file diff --git a/.jhipster/SepaMandate.json b/.jhipster/SepaMandate.json deleted file mode 100644 index 5879f6ed..00000000 --- a/.jhipster/SepaMandate.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "name": "SepaMandate", - "fields": [ - { - "fieldName": "reference", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength", - "unique", - "required" - ], - "fieldValidateRulesMaxlength": 40 - }, - { - "fieldName": "iban", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 34 - }, - { - "fieldName": "bic", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 11 - }, - { - "fieldName": "grantingDocumentDate", - "fieldType": "LocalDate", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "revokationDocumentDate", - "fieldType": "LocalDate" - }, - { - "fieldName": "validFromDate", - "fieldType": "LocalDate", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "validUntilDate", - "fieldType": "LocalDate" - }, - { - "fieldName": "lastUsedDate", - "fieldType": "LocalDate" - }, - { - "fieldName": "remark", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 160 - } - ], - "relationships": [ - { - "relationshipType": "many-to-one", - "otherEntityName": "customer", - "otherEntityRelationshipName": "sepamandate", - "relationshipValidateRules": "required", - "relationshipName": "customer", - "otherEntityField": "prefix" - } - ], - "changelogDate": "20190507105336", - "entityTableName": "sepa_mandate", - "dto": "mapstruct", - "pagination": "infinite-scroll", - "service": "serviceClass", - "jpaMetamodelFiltering": true, - "fluentMethods": true, - "clientRootFolder": "", - "applications": "*" -} \ No newline at end of file diff --git a/.jhipster/Share.json b/.jhipster/Share.json deleted file mode 100644 index 206438bd..00000000 --- a/.jhipster/Share.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "Share", - "fields": [ - { - "fieldName": "documentDate", - "fieldType": "LocalDate", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "valueDate", - "fieldType": "LocalDate", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "action", - "fieldType": "ShareAction", - "fieldValues": "SUBSCRIPTION,CANCELLATION", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "quantity", - "fieldType": "Integer", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "remark", - "fieldType": "String", - "fieldValidateRules": [ - "maxlength" - ], - "fieldValidateRulesMaxlength": 160 - } - ], - "relationships": [ - { - "relationshipType": "many-to-one", - "otherEntityName": "membership", - "otherEntityRelationshipName": "share", - "relationshipValidateRules": "required", - "relationshipName": "membership", - "otherEntityField": "admissionDocumentDate" - } - ], - "changelogDate": "20190507105334", - "entityTableName": "share", - "dto": "mapstruct", - "pagination": "infinite-scroll", - "service": "serviceClass", - "jpaMetamodelFiltering": true, - "fluentMethods": true, - "clientRootFolder": "", - "applications": "*" -} \ No newline at end of file diff --git a/.jhipster/UserRoleAssignment.json b/.jhipster/UserRoleAssignment.json deleted file mode 100644 index 493221c3..00000000 --- a/.jhipster/UserRoleAssignment.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "UserRoleAssignment", - "fields": [ - { - "fieldName": "entityTypeId", - "fieldType": "String", - "fieldValidateRules": [ - "required", - "maxlength" - ], - "fieldValidateRulesMaxlength": 32 - }, - { - "fieldName": "entityObjectId", - "fieldType": "Long", - "fieldValidateRules": [ - "required" - ] - }, - { - "fieldName": "assignedRole", - "fieldType": "UserRole", - "fieldValues": "HOSTMASTER,ADMIN,SUPPORTER,CONTRACTUAL_CONTACT,FINANCIAL_CONTACT,TECHNICAL_CONTACT,CUSTOMER_USER", - "fieldValidateRules": [ - "required" - ] - } - ], - "relationships": [ - { - "relationshipType": "many-to-one", - "otherEntityName": "user", - "otherEntityRelationshipName": "required", - "relationshipName": "user", - "otherEntityField": "login" - } - ], - "changelogDate": "20190507105342", - "entityTableName": "user_role_assignment", - "dto": "no", - "pagination": "infinite-scroll", - "service": "serviceClass", - "jpaMetamodelFiltering": true, - "fluentMethods": true, - "clientRootFolder": "", - "applications": "*" -} \ No newline at end of file diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 151fcf79..00000000 --- a/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -target -package-lock.json diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 6fd4aa12..00000000 --- a/.prettierrc +++ /dev/null @@ -1,12 +0,0 @@ -# Prettier configuration - -printWidth: 140 -singleQuote: true -tabWidth: 4 -useTabs: false - -# js and ts rules: -arrowParens: avoid - -# jsx and tsx rules: -jsxBracketSameLine: false diff --git a/.yo-rc.json b/.yo-rc.json deleted file mode 100644 index 9b35dabe..00000000 --- a/.yo-rc.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "generator-jhipster": { - "promptValues": { - "packageName": "org.hostsharing.hsadminng", - "nativeLanguage": "de" - }, - "jhipsterVersion": "5.8.2", - "applicationType": "monolith", - "baseName": "hsadminNg", - "packageName": "org.hostsharing.hsadminng", - "packageFolder": "org/hostsharing/hsadminng", - "serverPort": "8080", - "authenticationType": "jwt", - "cacheProvider": "ehcache", - "enableHibernateCache": false, - "websocket": false, - "databaseType": "sql", - "devDatabaseType": "h2Memory", - "prodDatabaseType": "postgresql", - "searchEngine": false, - "messageBroker": false, - "serviceDiscoveryType": false, - "buildTool": "gradle", - "enableSwaggerCodegen": true, - "jwtSecretKey": "ZDFlMDUzODIzMTUzZDEwZjExN2E5ZjAzY2VhZmYzNDE1YjhlYWUxZGRhMGU3ODZiNjRkNjVlNzEwZjExYWY4YzczM2NlYzI5YWE1OTRkNWM0YThlYjZjZjA5Zjc5YWJkOTgzYjdhZjQxZWQyZGUyYjFlYjI5ZDE3NmE4M2UzYjQ=", - "clientFramework": "angularX", - "useSass": false, - "clientPackageManager": "npm", - "testFrameworks": ["cucumber"], - "jhiPrefix": "jhi", - "entitySuffix": "", - "dtoSuffix": "DTO", - "otherModules": [], - "enableTranslation": true, - "nativeLanguage": "de", - "languages": ["de", "en"] - } -} diff --git a/Glossary.md b/Glossary.md new file mode 100644 index 00000000..2757ecc0 --- /dev/null +++ b/Glossary.md @@ -0,0 +1,18 @@ +# hsadminNg Glossary + +### Business Object + +Represents an object from the + +### Tenant + +The RBAC + +### RBAC + +abbreviation for *Role Based Access Control* + +### Role Based Access Control (RBAC) + +A system to control access to business objects by defining users, roles, and permissions. +For more information see diff --git a/JHIPSTER.md b/JHIPSTER.md deleted file mode 100644 index 7ae9184c..00000000 --- a/JHIPSTER.md +++ /dev/null @@ -1,196 +0,0 @@ -# hsadminNg - -This application was generated using JHipster 5.8.2, you can find documentation and help at [https://www.jhipster.tech/documentation-archive/v5.8.2](https://www.jhipster.tech/documentation-archive/v5.8.2). - -## Development - -Before you can build this project, you must install and configure the following dependencies on your machine: - -1. [Node.js][]: We use Node to run a development web server and build the project. - Depending on your system, you can install Node either from source or as a pre-packaged bundle. - -After installing Node, you should be able to run the following command to install development tools. -You will only need to run this command when dependencies change in [package.json](package.json). - - npm install - -We use npm scripts and [Webpack][] as our build system. - -Run the following commands in two separate terminals to create a blissful development experience where your browser -auto-refreshes when files change on your hard drive. - - ./gradlew - npm start - -Npm is also used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by -specifying a newer version in [package.json](package.json). You can also run `npm update` and `npm install` to manage dependencies. -Add the `help` flag on any command to see how you can use it. For example, `npm help update`. - -The `npm run` command will list all of the scripts available to run for this project. - -### Service workers - -Service workers are commented by default, to enable them please uncomment the following code. - -- The service worker registering script in index.html - -```html - -``` - -Note: workbox creates the respective service worker and dynamically generate the `service-worker.js` - -### Managing dependencies - -For example, to add [Leaflet][] library as a runtime dependency of your application, you would run following command: - - npm install --save --save-exact leaflet - -To benefit from TypeScript type definitions from [DefinitelyTyped][] repository in development, you would run following command: - - npm install --save-dev --save-exact @types/leaflet - -Then you would import the JS and CSS files specified in library's installation instructions so that [Webpack][] knows about them: -Edit [src/main/webapp/app/vendor.ts](src/main/webapp/app/vendor.ts) file: - -``` -import 'leaflet/dist/leaflet.js'; -``` - -Edit [src/main/webapp/content/css/vendor.css](src/main/webapp/content/css/vendor.css) file: - -``` -@import '~leaflet/dist/leaflet.css'; -``` - -Note: there are still few other things remaining to do for Leaflet that we won't detail here. - -For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][]. - -### Using angular-cli - -You can also use [Angular CLI][] to generate some custom client code. - -For example, the following command: - - ng generate component my-component - -will generate few files: - - create src/main/webapp/app/my-component/my-component.component.html - create src/main/webapp/app/my-component/my-component.component.ts - update src/main/webapp/app/app.module.ts - -### Doing API-First development using openapi-generator - -[OpenAPI-Generator]() is configured for this application. You can generate API code from the `src/main/resources/swagger/api.yml` definition file by running: - -```bash -./gradlew openApiGenerate -``` - -Then implements the generated delegate classes with `@Service` classes. - -To edit the `api.yml` definition file, you can use a tool such as [Swagger-Editor](). Start a local instance of the swagger-editor using docker by running: `docker-compose -f src/main/docker/swagger-editor.yml up -d`. The editor will then be reachable at [http://localhost:7742](http://localhost:7742). - -Refer to [Doing API-First development][] for more details. - -## Building for production - -To optimize the hsadminNg application for production, run: - - ./gradlew -Pprod clean bootWar - -This will concatenate and minify the client CSS and JavaScript files. It will also modify `index.html` so it references these new files. -To ensure everything worked, run: - - java -jar build/libs/*.war - -Then navigate to [http://localhost:8080](http://localhost:8080) in your browser. - -Refer to [Using JHipster in production][] for more details. - -## Testing - -To launch your application's tests, run: - - ./gradlew test - -### Client tests - -Unit tests are run by [Jest][] and written with [Jasmine][]. They're located in [src/test/javascript/](src/test/javascript/) and can be run with: - - npm test - -For more information, refer to the [Running tests page][]. - -### Code quality - -Sonar is used to analyse code quality. You can start a local Sonar server (accessible on http://localhost:9001) with: - -``` -docker-compose -f src/main/docker/sonar.yml up -d -``` - -Then, run a Sonar analysis: - -``` -./gradlew -Pprod clean test sonarqube -``` - -For more information, refer to the [Code quality page][]. - -## Using Docker to simplify development (optional) - -You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services. - -For example, to start a postgresql database in a docker container, run: - - docker-compose -f src/main/docker/postgresql.yml up -d - -To stop it and remove the container, run: - - docker-compose -f src/main/docker/postgresql.yml down - -You can also fully dockerize your application and all the services that it depends on. -To achieve this, first build a docker image of your app by running: - - ./gradlew bootWar -Pprod jibDockerBuild - -Then run: - - docker-compose -f src/main/docker/app.yml up -d - -For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the docker-compose sub-generator (`jhipster docker-compose`), which is able to generate docker configurations for one or several JHipster applications. - -## Continuous Integration (optional) - -To configure CI for your project, run the ci-cd sub-generator (`jhipster ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information. - -[jhipster homepage and latest documentation]: https://www.jhipster.tech -[jhipster 5.8.2 archive]: https://www.jhipster.tech/documentation-archive/v5.8.2 -[using jhipster in development]: https://www.jhipster.tech/documentation-archive/v5.8.2/development/ -[using docker and docker-compose]: https://www.jhipster.tech/documentation-archive/v5.8.2/docker-compose -[using jhipster in production]: https://www.jhipster.tech/documentation-archive/v5.8.2/production/ -[running tests page]: https://www.jhipster.tech/documentation-archive/v5.8.2/running-tests/ -[code quality page]: https://www.jhipster.tech/documentation-archive/v5.8.2/code-quality/ -[setting up continuous integration]: https://www.jhipster.tech/documentation-archive/v5.8.2/setting-up-ci/ -[node.js]: https://nodejs.org/ -[yarn]: https://yarnpkg.org/ -[webpack]: https://webpack.github.io/ -[angular cli]: https://cli.angular.io/ -[browsersync]: http://www.browsersync.io/ -[jest]: https://facebook.github.io/jest/ -[jasmine]: http://jasmine.github.io/2.0/introduction.html -[protractor]: https://angular.github.io/protractor/ -[leaflet]: http://leafletjs.com/ -[definitelytyped]: http://definitelytyped.org/ -[openapi-generator]: https://openapi-generator.tech -[swagger-editor]: http://editor.swagger.io -[doing api-first development]: https://www.jhipster.tech/documentation-archive/v5.8.2/doing-api-first-development/ diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index bb4438e7..00000000 --- a/Jenkinsfile +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env groovy - -node { - withEnv(["PATH=$HOME/bin:$PATH"]) { - stage('checkout') { - checkout scm - } - - stage('check java') { - sh "java -version" - } - - stage('clean+spotless') { - sh "chmod +x gradlew" - sh "./gradlew clean spotlessCheck --no-daemon" - } - - stage('npm install') { - sh "./gradlew npm_install -PnodeInstall --no-daemon" - } - - stage('backend tests') { - try { - sh "./gradlew test -PnodeInstall --no-daemon" - } catch (err) { - throw err - } finally { - junit '**/build/**/TEST-*.xml' - } - } - - stage('backend check') { - try { - sh "./gradlew check -PnodeInstall --no-daemon" - } catch (err) { - throw err - } finally { - archiveArtifacts artifacts: '**/build/reports/jacoco/test/html/', fingerprint: true - } - } - - stage('frontend tests') { - try { - sh "./gradlew npm_run_test -PnodeInstall --no-daemon" - } catch (err) { - throw err - } finally { - junit '**/build/test-results/TESTS-*.xml' - } - } - - stage('packaging') { - sh "./gradlew bootWar -x test -Pprod -PnodeInstall --no-daemon" - archiveArtifacts artifacts: '**/build/libs/*.war', fingerprint: true - } - } -} diff --git a/README.md b/README.md index f7ab026e..8ff1af54 100644 --- a/README.md +++ b/README.md @@ -1,350 +1,77 @@ # hsadminNg Development - - - -- [Setting up the Development Environment](#setting-up-the-development-environment) -- [Frequent Tasks](#frequent-tasks) - - [Building the Application with Test Execution](#building-the-application-with-test-execution) - - [Starting the Application](#starting-the-application) - - [Running JUnit tests with branch coverage](#running-junit-tests-with-branch-coverage) -- [HOWTO Commits](#howto-commits) - - [Creating HOWTO Commits](#creating-howto-commits) -- [Special Build Tasks](#special-build-tasks) - - [Spotless Formatting](#spotless-formatting) - - [Mutation Testing PiTest](#mutation-testing-pitest) - - [Git Workflow for JHipster Generator](#git-workflow-for-jhipster-generator) - - [Generating the Table of Contents for Markdown](#generating-the-table-of-contents-for-markdown) - - - ## Setting up the Development Environment -You'll often need to execute `./gradlew`, therefore we suggest to define this alias: +### PostgreSQL Server - alias gw='./gradlew' +So far the spike contains almost only PostgreSQL Code. +All you need so far, is a PostgreSQL database, for now with full admin rights. +The easiest way to set it up is using docker: -TODO: Instructions for setting up the dev environment from scratch. +Initially, pull an image compatible to current PostgreSQL version of Hostsharing: -## Frequent Tasks + docker pull postgres:13.7-bullseye -### Building the Application with Test Execution +Create and run a container with the given PostgreSQL version: - gw build + docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:13.7-bullseye -### Starting the Application +To check if the PostgreSQL container is running, the following command should list a container with the name "hsadmin-ng-postgres": -To use an **H2 in-memory database** populated with sample-data. + docker container ls - gw bootRun +Stop the PostgreSQL container: + + docker stop hsadmin-ng-postgres -To use an **H2 file-based database**, start the application with the h2file profile: +Start the PostgreSQL container again: - gw bootRun -Ph2file - gw bootRun -Ph2file -Psample-data # populated with sample data + docker container start hsadmin-ng-postgres -To use a **local Postgres database**, first prepare your environment: +Remove the PostgreSQL container: - export HSADMINNG_DB_URL='jdbc:postgresql://localhost:5432/DBNAME' - export HSADMINNG_DB_USER='DBUSER' - export HSADMINNG_DB_PASS='DBPASS' + docker rm hsadmin-ng-postgres -Where `DBNAME`, `DBUSER` and `DBPASS` are replaced by your credentials. +After the PostgreSQL container is removed, you need to create it again as shown in "Create and run ..." above. -Then start the application with the pgsql profile: +### Markdown with PlantUML plugin - gw bootRun -Ppgsql - gw bootRun -Ppgsql -Psample-data # populated with sample data +Can you see the following diagram? -To use a **remote Postgres database** on a Hostsharing server, +```plantuml +@startuml +me -> you: Can you see this diagram? +you -> me: Sorry, I don't :-( +me -> you: Install some tooling! +@enduml +``` - autossh -M 0 -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" \ - -f -N -L 55432:127.0.0.1:5432 "xyz00@xyz.hostsharing.net" +If not, you need to install some tooling. -Then prepare your environment, e.g. like this: +#### for IntelliJ IDEA (or derived products) - export HSADMINNG_DB_URL='jdbc:postgresql://localhost:55432/xyz00_hsadminng' - export HSADMINNG_DB_USER='xyz00_hsadminng' - export HSADMINNG_DB_PASS='whatever' +You just need the bundled Markdown plugin enabled and install and activate the PlantUML plugin in its settings: -In all cases, you can also **specify the port** to used for the application via environment: +jetbrains://idea/settings?name=Languages+%26+Frameworks--Markdown - SERVER_PORT=8081 gw bootRun ... +You might also need to install Graphviz on your operating system. +For Debian-based Linux systems this might work: -For starting the JVM of the application in **debug-mode**, add `--debug-jvm` to any of the options above, e.g. +```sh +sudo apt install graphviz +``` - gw bootRun -Ppgsql -Psample-data --debug-jvm -### Running JUnit tests with branch coverage +### Ubuntu Linux command line -#### for IntelliJ IDEA +```sh +sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive-extra-utils texlive-latex-extra pandoc-plantuml-filter +``` -see: https://confluence.jetbrains.com/display/IDEADEV/IDEA+Coverage+Runner +```sh +pandoc --filter pandoc-plantuml rbac.md -o rbac.pdf +``` -Either apply it to specific test configurations or, -better, delete the previous test configurations and amend the JUnit template. +### for other IDEs / operating systems -## HOWTO Commits - -There are git tags on some commits which show how to add certain features. - -Find all of such tags with: - - git tag | grep HOWTO - -### Creating HOWTO Commits - -If you want to add such a commit, make sure that it contains no clutter -(no changes which are not necessary for whatever the commit is about to explain), -and is complete with all unit tests, code coverage, pitest and other checks. -Otherwise the next developer would run into the same problems again. - -One way to keep the commit clean, is to develop it on a local branch. -If any other changes (e.g. bugfixes, API extensions etc.) are necessary, -apply these only to the master or cherry-pick just these to the master, -then rebase your local branch. Do not forget to run all checks locally: - - gw clean check pitest # might need over an hour - -(Check the PiTest section for speeding up mutation testing.) - -To create and push a new tag use: - - git tag HOWTO-... master - git push origin HOWTO-... - -To moved an existing the tag to another commit (here current master again), do this: - - git tag --force HOWTO-... master - git push --force origin HOWTO-... - -## Special Build Tasks - -Besides common build tasks like `build`, `test` or `bootRun` this projects has some not so common tasks which are explained in this section. - -### Spotless Formatting - -To make sure that no IDE auto-formatter destroys the git history of any file and -especially to avoid merge conflicts from JHipster generated files after these had been changed, -we are using a standard formatter enforced by _spotless_, which is based on the standard Eclipse formatter. - -The rules can be checked and applied with these commands: - - gw spotlessCheck - gw spotlessApply - -The spotlessCheck task is included as an early step in our Jenkins build pipeline. -Therefore wrong formatting is automatically detected. - -Our configuration can be found under the directory `cfg/spotless`. -Currently we only have specific rules for _\*.java_-files and their import-order. - -#### Our Changes to the Standard Eclipse Formatter - -We amended the Standard Eclipse Formatter in these respects: - -- Lines of code are never joined, thus the developer has control about linebreaks, - which is important for readability in some implementations like toString(). -- Lines in comments are never joined either, because that often destroys readable stucture. -- Parts of files can be excluded from getting formatted, by using `@formatter:off` and `@formatter:on` in a comment. - See for example in class `SecurityConfiguration`. - -#### Pre-Commit Hook - -If you like, you could add this code to the _pre-commit or \_pre_push_ hook\_ in your `.git/hooks` directory: - - if ! ./gradlew spotlessCheck; then - exit 1 - fi - -#### The Tagged Spotless Commit - -The commit which introduces the spotless configuration is tagged. -Through this tag it can easily be cherry-picked in the JHipster workflow. - -If you need to amend the commit tagged 'spotless', e.g. to change the spotless configuration, -it can be done with these steps: - - git tag REAL-HEAD - git reset --hard spotless^ - git cherry-pick -n spotless - ... - git add . - # do NOT run: gw spotlessApply yet! - # for the case you have a commit hook which runs spotlessCheck: - git commit --no-verify - git tag --force spotless - git push --no-verify origin spotless - git reset --hard REAL-HEAD - git tag -d REAL-HEAD - -### Mutation Testing PiTest - - ./gradlew pitest - -Runs (almost) all JUnit tests under mutation testing. -Mutation testing is a means to determine the quality of the tests. - -On Jenkins, the results can be found in the build artifacts under: - -- https://ci.hostsharing.net/job/hsadmin-ng-pitest/XX/artifact/build/reports/pitest/index.html - -Where XX is the build number. Or for the latest build under: - -- https://ci.hostsharing.net/job/hsadmin-ng-pitest/lastCompletedBuild/artifact/build/reports/pitest/index.html - -#### Some Background Information on Mutation Testing - -PiTest does it with these steps: - -- initially PiTest checks which production code is executed by which tests -- if the tests don't pass, it stops -- otherwise the production code is 'mutated' and PiTest checks whether this makes a test fail ('mutant killed') -- Finally it checks thresholds for coverage and mutant killing. - -More information about can be found here: - -- PiTest: http://pitest.org/ -- gradle-plugin: https://gradle-pitest-plugin.solidsoft.info/ - -#### How to Configure PiTest - -These thresholds can be configured in `build.gradle`, -but we should generally not lower these. - -There is also a list of excluded files, all generated by JHipster or MapStruct, not containing any changes by us. - -As you might figure, mutation testing is CPU-hungry. -To limit load in our Jenkins build server, it only uses 2 CPU threads, thus it needs over an hour. - -If you want to spend more CPU threads on your local system, you can change that via command line: - - gw pitest -Doverride.pitest.threads=7 - -I suggest to leave one CPU thread for other tasks or your might lag extremely. - -### Git Workflow for JHipster Generator - -The following workflow steps make sure that - -- JHipster re-imports work properly, -- the git history of changes to the JDL-files, the generated code and the master is comprehensible, -- and merging newly generated code to the master branch is smooth. - -It uses a git branch `jhipster-generated` to track the history of the JDL model file and the generated source code. -Applying commits which contain non-generated changes to that branch breaks the normal git history for generated files. -Therefore, this documentation is also not available in that branch. -Thus: - -**MANUAL STEP before starting:** Copy this workflow documentation, because this file will be gone once you switched the branch. - -| WARNING: The following steps are just a guideline. You should understand what you are doing! | -| -------------------------------------------------------------------------------------------- | - - -#### 1. Preparing the `jhipster-generated` git Branch - -This step assumes that the latest `*.jdl` files are on the `HEAD` of the `jhipster-generated` git branch. -On a re-import of a JDL-file, JHipster does not remove any generated classes which belong to entities deleted from the JDL-file. -Therefore, the project has to be reset to a clean state before changes to the JDL file can be re-imported. -We have not yet finally tested a simplified workflow for just adding new entities or properties. - -A git tag `jdl-base` is assumed to sit on the base commit after the application was generated, but before any entities were imported. - - git checkout jhipster-generated - git pull - git tag REAL-HEAD - git reset --hard jdl-base - git clean -f -d - git cherry-pick -n spotless - git reset --soft REAL-HEAD - git checkout REAL-HEAD src/main/jdl/customer.jdl - git checkout REAL-HEAD src/main/jdl/accessrights.jdl - git checkout REAL-HEAD src/main/jdl/... # once there are more - git tag -d REAL-HEAD - -#### 2. Amending and Re-Importing the JDL - -**MANUAL STEP:** First apply all necessary changes to the JDL files. -Then re-import like this: - - # (Re-) Importing - jhipster import-jdl src/main/jdl/customer.jdl - jhipster import-jdl src/main/jdl/accessrights.jdl - jhipster import-jdl src/main/jdl/... # once there are more - -For smoothly being able to merge, we need the same formatting in the generated code as on the master: - - gw spotlessApply - -#### 3. Committing our Changes - - git add . - git commit -m"..." - -#### 4. Merging our Changes to the `master` Branch - - git checkout master - git pull - -**MANUAL STEP:** If you've renamed any identifiers, use the refactoring feature of your IDE to rename in master as well. -To avoid oodles of merge-conflicts, you need to do that **BEFORE MERGING!** -Commit any of such changes, if any. - -Now we can finally merge our changes to master. - - git merge jhipster-generated - -It's a good idea doing this step in an IDE because it makes conflict resolving much easier. -Typical merge conflicts stem from: - -- Random numbers in test data of `*IntTest.java` files. -- Timestamps in Liquibase-xml-Files. - -Now, I suggest to run all tests locally: - - gw clean test - -Once everything works again, we can push our new version: - - git push - -#### 5. General Aftermath - -Think about which additional code could be effected by your JDL-changes! -Files which are not at all in the `jhipster-generated` branch, don't show conflicts even though they might need changes. - -Here some examples for amendments to be done: - -- in `historicization_*.xml`: the columns or their constraints -- `sampledata/*.xml/csv` - -If you find more of such general cases, please add them here! - -#### 6. Special Aftermath for new Entities - -Because we have added quite some functionality, after introducing new entities, there is a lot more to amend. -Here some issues to consider: - -- add sample-data for the new entity -- internal (Angular) frontend: add table filters -- internal (Angular) frontend: amend input fields for multiline, if applicable -- internal (Angular) frontend: check if dates are properly formatted -- \*Mapper: add displayLabel for entity itself and parents -- \*DTO: add access-right annotations with customized JSON serializer/deserializer -- Validator: implement entity-based validator and call it in the generated service -- external API: add new type to client library - -WARNING: This list is most likely incomplete. Pleas add any new found issue! - -For many of these issues look for HOWTO-commits in git or HOWTO comments in the source code. - -### Generating the Table of Contents for Markdown - -This README file contains a table of contents generated by _doctoc_. -It's quite simple to use: - -npm install -g doctoc -doctoc --maxlevel 3 README.md - -Further information can be found [https://github.com/thlorenz/doctoc/blob/master/README.md](on the _doctoc_ github page). +If you have figured out how it works, please add instructions above this section. diff --git a/adr/2022-07-18.row-level-security-mechanism.md b/adr/2022-07-18.row-level-security-mechanism.md new file mode 100644 index 00000000..51b72245 --- /dev/null +++ b/adr/2022-07-18.row-level-security-mechanism.md @@ -0,0 +1,160 @@ +# Use VIEWs with JOIN into Permission-Assignments for Row-Level-Security + +**Status:** +- [x] proposed by Michael Hönnig +- [ ] accepted by (Participants) +- [ ] rejected by (Participants) +- [ ] superseded by (superseding ADR) + +## Context and Problem Statement + +We need to decide how to apply the access rules defined in our RBAC system to the visibility of table rows for the accessing user. + +The core problem here is, that in our RBAC system, determining the permissions of the accessing user has to consider a hierarchy of roles. + +### Technical Background + +The session variable `hsadminng.currentUser` contains the accessing (domain-level) user, which is unrelated to the PostgreSQL user). + +Given is a stored function `isPermissionGrantedToSubject` which detects if the accessing user has a given permission (e.g. 'view'). + +Given is also a stored function `queryAllPermissionsOfSubjectId` which returns the flattened view to all permissions assigned to the given accessing user. + +In the following code snippets `customer` is just an example domain table. + +## Considered Options + +* Perform Visibility-Checks programmatically in the Backend +* Add Visibility-Checks in the Backend +* POLICY with ENABLE ROW LEVEL SECURITY +* VIEW-RULE with ON SELECT DO INSTEAD +* VIEW with JOIN into Flattened Permissions + +### Perform Visibility-Checks programmatically in the Backend + +In this solution, the database ignores row level visibility and returns all rows which match a given query. Afterwards, the result is filtered programmatically with Java-code in the backend. + +#### Advantages + +Very flexible access, programmatic, rules could be implemented. + +The role-hierarchy and permissions for currently logged-in users user could be cached in the backend. + +The access logic can be tested in pure Java unit tests. + +At least regarding this aspect, an in-memory database could be used for integration testing; though the recursive Role-evaluation uses PostgreSQL features anyway. + +#### Disadvantages + +It's inefficient when initial query is not very restrictive, e.g. as on overview pages in a frontend, which often show all accessible objects, large parts or even whole database tables need to be transferred from the database to the backend. + +It's error-prone and security leaks can happen too easily, because after every query the access rights for all participating joins have to be considered. + +### Add Visibility-Checks in the Backend + +In this solution again, the database ignores row level visibility and returns all rows which match a given query. And the backend adds filter conditions to each query sent to the database. + +#### Advantages + +At least regarding this aspect, an in-memory database could be used for integration testing. + +#### Disadvantages + +It's error-prone and security leaks can happen too easily, because for every query the access rights for all participating joins have to be considered. + +### POLICY with ENABLE ROW LEVEL SECURITY + +For restricted DB-users, which are used by the backend, access to rows is filtered using a policy: + + SET SESSION AUTHORIZATION DEFAULT; + CREATE ROLE restricted; + GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; + ALTER TABLE customer ENABLE ROW LEVEL SECURITY; + CREATE POLICY customer_policy ON customer + FOR SELECT + TO restricted + USING ( + isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserId()) + ); + + SET SESSION AUTHORIZATION restricted; + SET hsadminng.currentUser TO 'alex@example.com'; + SELECT * from customer; -- will only return visible rows + +#### Advantages + +Using POLICY together with ENABLE ROW LEVEL SECURITY is the PostgreSQL native mechanism to control access to data on the role level. Therefore, it looked like an obvious and elegant solution. + +Every access at from the backend is under access control at the database level. + +### Disadvantages + +Unfortunately security mechanisms in PostgreSQL prevent the query optimizer to work well beyond ownership barriers (session user vs. table owner) and a SELECT from a table with 1 million objects needed over 30 seconds with our hierarchical RBAC policy. + +We are bound to PostgreSQL, including integration tests and testing the RBAC system itself. + +### VIEW-RULE with ON SELECT DO INSTEAD + + SET SESSION SESSION AUTHORIZATION DEFAULT; + CREATE VIEW cust_view AS + SELECT * FROM customer; + CREATE OR REPLACE RULE "_RETURN" AS + ON SELECT TO cust_view + DO INSTEAD + SELECT * FROM customer WHERE isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserId()); + + SET SESSION AUTHORIZATION restricted; + SET hsadminng.currentUser TO 'alex@example.com'; + SELECT * from customer; -- will only return visible rows + +#### Advantages + +Every access at from the backend is under access control at the database level. + +Also using ON UPDATE etc., original tables could be completely hidden from the backend, and thus improved security. + +### Disadvantages + +Unfortunately security mechanisms in PostgreSQL prevent the query optimizer to work well beyond ownership barriers (session user vs. table owner) and a SELECT from a table with 1 million objects needed over 30 seconds with our hierarchical RBAC policy. + +We are bound to PostgreSQL, including integration tests and testing the RBAC system itself. + +An extra view needed for every table. + + +### VIEW with JOIN into flattened permissions + +We do not access the tables directly from the backend, but via views which join the flattened permissions + + SET SESSION SESSION AUTHORIZATION DEFAULT; + CREATE OR REPLACE VIEW cust_view AS + SELECT c.id, c.reference, c.prefix + FROM customer AS c + JOIN queryAllPermissionsOfSubjectId(currentUserId()) AS p + ON p.tableName='customer' AND p.rowId=c.id AND p.op='view'; + GRANT ALL PRIVILEGES ON cust_view TO restricted; + + SET SESSION SESSION AUTHORIZATION restricted; + SET hsadminng.currentUser TO 'alex@example.com'; + SELECT * from cust_view; -- will only return visible rows + +Alternatively the JOIN could also be applied in a "ON SELECT DO INSTEAD"-RULE, if there is any advantage for later features. + +#### Advantages + +Every access at from the backend is under access control at the database level. + +No special PostgreSQL features needed; though the recursive Role-evaluation uses PostgreSQL features anyway. + +Very fast, on my laptop a SELECT * FROM a table with 1 million rows just took about 50ms. + +Also using ON UPDATE etc., original tables could be completely hidden from the backend, and thus improved security. + +### Disadvantages + +An extra view needed for every table. + + +## Decision Outcome + +We chose the option **"VIEW with JOIN into flattened permissions"** because it supports the best combination of performance and security with almost no disadvantge. diff --git a/angular.json b/angular.json deleted file mode 100644 index 85278e71..00000000 --- a/angular.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "hsadmin-ng": { - "root": "", - "sourceRoot": "src/main/webapp", - "projectType": "application", - "architect": {} - } - }, - "defaultProject": "hsadmin-ng", - "cli": { - "packageManager": "npm" - }, - "schematics": { - "@schematics/angular:component": { - "inlineStyle": true, - "inlineTemplate": false, - "spec": false, - "prefix": "jhi", - "styleExt": "css" - }, - "@schematics/angular:directive": { - "spec": false, - "prefix": "jhi" - }, - "@schematics/angular:guard": { - "spec": false - }, - "@schematics/angular:pipe": { - "spec": false - }, - "@schematics/angular:service": { - "spec": false - } - } -} diff --git a/build-cucumber.gradle b/build-cucumber.gradle deleted file mode 100644 index edc24fa2..00000000 --- a/build-cucumber.gradle +++ /dev/null @@ -1,24 +0,0 @@ -// Behaviour tests based on a deployed application. - -task cucumberTest(type: Test) { - description = "Execute cucumber BDD tests." - group = "verification" - include '**/CucumberTest*' - - // uncomment if the tests reports are not generated - // see https://github.com/jhipster/generator-jhipster/pull/2771 and https://github.com/jhipster/generator-jhipster/pull/4484 - // ignoreFailures true - reports.html.enabled = false -} - -check.dependsOn cucumberTest -task testReport(type: TestReport) { - destinationDir = file("$buildDir/reports/tests") - reportOn test -} - -task cucumberTestReport(type: TestReport) { - destinationDir = file("$buildDir/reports/tests") - reportOn cucumberTest -} - diff --git a/build-jacoco.gradle b/build-jacoco.gradle deleted file mode 100644 index 4b8de1ee..00000000 --- a/build-jacoco.gradle +++ /dev/null @@ -1,84 +0,0 @@ -// Checks code coverage of JUnit based tests. - -apply plugin: 'jacoco' - -jacoco { - toolVersion = "0.8.3a" -} - -test.finalizedBy jacocoTestReport -check.dependsOn jacocoTestCoverageVerification - -// Only for purely JHipster/MapStruct generated classes. -// Please do NOT add any self coded classes! -// Keep in mind, git will blame you ;-) -def jhipsterGeneratedClassesWithDecentCoverage = [ - 'org.hostsharing.hsadminng.repository.CustomAuditEventRepository', - 'org.hostsharing.hsadminng.service.ContactQueryService', - 'org.hostsharing.hsadminng.service.UserService', - 'org.hostsharing.hsadminng.service.CustomerContactQueryService' -] - -// Only for purely JHipster/MapStruct generated classes. -// Please do NOT add any self coded classes! -// Keep in mind, git will blame you ;-) -def jhipsterGeneratedClassesWithLowCoverage = [ - 'org.hostsharing.hsadminng.service.MailService', - 'org.hostsharing.hsadminng.security.SecurityUtils', - 'org.hostsharing.hsadminng.config.DefaultProfileUtil', - 'org.hostsharing.hsadminng.config.WebConfigurer', - 'org.hostsharing.hsadminng.web.rest.AccountResource', - 'org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator', - 'org.hostsharing.hsadminng.web.rest.errors.CustomParameterizedException', - 'org.hostsharing.hsadminng.config.audit.AuditEventConverter', - 'org.hostsharing.hsadminng.security.jwt.TokenProvider', - 'org.hostsharing.hsadminng.aop.logging.LoggingAspect', - 'org.hostsharing.hsadminng.HsadminNgApp', - '*.*QueryService', - '*.*Configuration', - '*MapperImpl', - '*Criteria', - '*_' -] - -def specialExceptions = [ - // lots of unreachable code due to error handling / verifications - 'org.hostsharing.hsadminng.service.accessfilter.JSonAccessFilter', - 'org.hostsharing.hsadminng.service.util.ReflectionUtil' -] - -jacocoTestCoverageVerification { - violationRules { - rule { - element = 'CLASS' - limit { - counter = 'BRANCH' - value = 'COVEREDRATIO' - // Increasing the threshold is fine, decreasing is not. - // Keep in mind, git will blame you ;-) - minimum = 0.95 - } - excludes = jhipsterGeneratedClassesWithDecentCoverage + jhipsterGeneratedClassesWithLowCoverage + specialExceptions - } - - rule { - element = 'CLASS' - limit { - counter = 'BRANCH' - value = 'COVEREDRATIO' - minimum = 0.80 - } - includes = jhipsterGeneratedClassesWithDecentCoverage - } - - rule { - element = 'CLASS' - limit { - counter = 'LINE' - value = 'COVEREDRATIO' - minimum = 0.85 - } - includes = specialExceptions - } - } -} diff --git a/build-pitest.gradle b/build-pitest.gradle deleted file mode 100644 index 2c6c5f83..00000000 --- a/build-pitest.gradle +++ /dev/null @@ -1,36 +0,0 @@ -// PiTest based mutation testing - -pitest { - targetClasses = ['org.hostsharing.hsadminng.*'] - - excludedClasses = [ - // Unit Testing Spring configurations makes little sense in most cases. - 'org.hostsharing.hsadminng.config.*', - 'org.hostsharing.hsadminng.ApplicationWebXml', - 'org.hostsharing.hsadminng.HsadminNgApp', - - // Unit testing this would need PowerMock and - // blackbox testing of random values has little value. - 'org.hostsharing.hsadminng.service.util.RandomUtil', - - // The following are mostly generated classes, - // as soon as we amend these, consider removing the exclude. - 'org.hostsharing.hsadminng.**Criteria', - 'org.hostsharing.hsadminng.**MapperImpl', - 'org.hostsharing.hsadminng.aop.logging.*', - 'org.hostsharing.hsadminng.web.rest.vm.*', - 'org.hostsharing.hsadminng.security.jwt.TokenProvider', - 'org.hostsharing.hsadminng.web.api.*' // API helpers, not the API itself - ] - threads = 2 - - // Do not set these limit lower! 96% each might sound great, but keep in mind: - // 91%*91% means that ~8% of the code are NOT properly covered by automated tests - // (100%-94%*96% = ~8%). Not counting defects which come through missing code :-) - coverageThreshold = 94 - mutationThreshold = 96 - - outputFormats = ['XML', 'HTML'] - timestampedReports = false - verbose = false -} diff --git a/build-spotless.gradle b/build-spotless.gradle deleted file mode 100644 index 7f2fe589..00000000 --- a/build-spotless.gradle +++ /dev/null @@ -1,15 +0,0 @@ -// apply and check standard formatting of files - -apply plugin: "com.diffplug.gradle.spotless" - -spotless { - // automatically format source files, see https://github.com/diffplug/spotless - java { - licenseHeader '// Licensed under Apache-2.0' - importOrderFile 'cfg/spotless/hsadminng.importorder' - eclipse().configFile 'cfg/spotless/eclipse_formatter.xml' - target 'src/main/**/*.java', 'src/test/**/*.java' - removeUnusedImports() - endWithNewline() - } -} diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 1902c6d5..00000000 --- a/build.gradle +++ /dev/null @@ -1,297 +0,0 @@ -import org.gradle.internal.os.OperatingSystem - -buildscript { - repositories { - mavenLocal() - mavenCentral() - maven { url "http://repo.spring.io/plugins-release" } - maven { url "https://plugins.gradle.org/m2/" } - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${spring_boot_version}" - classpath "io.spring.gradle:propdeps-plugin:0.0.10.RELEASE" - classpath "org.openapitools:openapi-generator-gradle-plugin:3.3.0" - classpath "gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.5.2" - //jhipster-needle-gradle-buildscript-dependency - JHipster will add additional gradle build script plugins here - - classpath "com.diffplug.spotless:spotless-plugin-gradle:3.22.0" - classpath 'org.owasp:dependency-check-gradle:4.0.2' - } -} - -plugins { - id "org.sonarqube" version "2.6.2" - id "net.ltgt.apt-eclipse" version "0.19" - id "net.ltgt.apt-idea" version "0.19" - id "net.ltgt.apt" version "0.19" - id "io.spring.dependency-management" version "1.0.6.RELEASE" - id "com.moowork.node" version "1.2.0" - id 'org.liquibase.gradle' version '2.0.1' - id 'info.solidsoft.pitest' version '1.4.0' - //jhipster-needle-gradle-plugins - JHipster will add additional gradle plugins here -} - -apply plugin: 'java' -apply plugin: 'org.owasp.dependencycheck' -sourceCompatibility=1.8 -targetCompatibility=1.8 - -apply plugin: 'maven' -apply plugin: 'org.springframework.boot' -apply plugin: 'war' -apply plugin: 'propdeps' -apply plugin: 'com.moowork.node' -apply plugin: 'io.spring.dependency-management' -apply plugin: 'idea' - -apply from: 'build-jacoco.gradle' -apply from: 'build-pitest.gradle' -apply from: 'build-spotless.gradle' - -idea { - module { - excludeDirs += files('node_modules') - } -} - -dependencyManagement { - imports { - mavenBom 'io.github.jhipster:jhipster-dependencies:' + jhipster_dependencies_version - //jhipster-needle-gradle-dependency-management - JHipster will add additional dependencies management here - } -} - -defaultTasks 'bootRun' - -group = 'org.hostsharing.hsadminng' -version = '0.0.1-SNAPSHOT' - -description = '' - -bootWar { - mainClassName = 'org.hostsharing.hsadminng.HsadminNgApp' -} - -war { - webAppDirName = 'build/www/' - enabled = true - extension = 'war.original' -} - -springBoot { - mainClassName = 'org.hostsharing.hsadminng.HsadminNgApp' -} - -if (OperatingSystem.current().isWindows()) { - // https://stackoverflow.com/questions/40037487/the-filename-or-extension-is-too-long-error-using-gradle - task classpathJar(type: Jar) { - dependsOn configurations.runtime - appendix = 'classpath' - - doFirst { - manifest { - attributes 'Class-Path': configurations.runtime.files.collect { - it.toURI().toURL().toString().replaceFirst(/file:\/+/, '/').replaceAll(' ', '%20') - }.join(' ') - } - } - } - - bootRun { - dependsOn classpathJar - doFirst { - classpath = files("$buildDir/classes/java/main", "$buildDir/resources/main", classpathJar.archivePath) - } - } -} - -test { - exclude '**/CucumberTest*' - - // uncomment if the tests reports are not generated - // see https://github.com/jhipster/generator-jhipster/pull/2771 and https://github.com/jhipster/generator-jhipster/pull/4484 - // ignoreFailures true - reports.html.enabled = false -} - -task cucumberTest(type: Test) { - description = "Execute cucumber BDD tests." - group = "verification" - include '**/CucumberTest*' - - // uncomment if the tests reports are not generated - // see https://github.com/jhipster/generator-jhipster/pull/2771 and https://github.com/jhipster/generator-jhipster/pull/4484 - // ignoreFailures true - reports.html.enabled = false -} - -check.dependsOn cucumberTest -task testReport(type: TestReport) { - destinationDir = file("$buildDir/reports/tests") - reportOn test -} - -task cucumberTestReport(type: TestReport) { - destinationDir = file("$buildDir/reports/tests") - reportOn cucumberTest -} - -apply from: 'gradle/docker.gradle' -apply from: 'gradle/sonar.gradle' -apply from: 'gradle/swagger.gradle' -//jhipster-needle-gradle-apply-from - JHipster will add additional gradle scripts to be applied here - -if (project.hasProperty('prod')) { - apply from: 'gradle/profile_prod.gradle' -} else { - apply from: 'gradle/profile_dev.gradle' -} - - -if (!project.hasProperty('runList')) { - project.ext.runList = 'main' -} - -project.ext.diffChangelogFile = 'src/main/resources/config/liquibase/changelog/' + new Date().format('yyyyMMddHHmmss') + '_changelog.xml' - -liquibase { - activities { - main { - driver '' - url '' - username 'hsadminNg' - password '' - changeLogFile 'src/main/resources/config/liquibase/master.xml' - defaultSchemaName '' - logLevel 'debug' - classpath 'src/main/resources/' - if (project.hasProperty('sample-data')) { - contexts 'sample-data' - } - } - diffLog { - driver '' - url '' - username 'hsadminNg' - password '' - changeLogFile project.ext.diffChangelogFile - referenceUrl 'hibernate:spring:org.hostsharing.hsadminng.domain?dialect=&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy' - defaultSchemaName '' - logLevel 'debug' - classpath "$buildDir/classes/java/main" - } - } - - runList = project.ext.runList -} - -configurations { - providedRuntime - compile.exclude module: "spring-boot-starter-tomcat" -} - -repositories { - mavenLocal() - mavenCentral() - jcenter() - //jhipster-needle-gradle-repositories - JHipster will add additional repositories -} - -dependencies { - // Use ", version: jhipster_dependencies_version, changing: true" if you want - // to use a SNAPSHOT release instead of a stable release - compile group: "io.github.jhipster", name: "jhipster-framework" - compile "org.springframework.boot:spring-boot-starter-cache" - compile "io.dropwizard.metrics:metrics-core" - compile 'io.micrometer:micrometer-registry-prometheus' - compile "net.logstash.logback:logstash-logback-encoder" - compile "com.fasterxml.jackson.datatype:jackson-datatype-hppc" - compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" - compile "com.fasterxml.jackson.datatype:jackson-datatype-hibernate5" - compile "com.fasterxml.jackson.core:jackson-annotations" - compile "com.fasterxml.jackson.core:jackson-databind" - compile "com.fasterxml.jackson.module:jackson-module-afterburner" - compile "javax.cache:cache-api" - compile "org.hibernate:hibernate-core" - compile "com.zaxxer:HikariCP" - compile "org.apache.commons:commons-lang3" - compile "commons-io:commons-io" - compile "javax.transaction:javax.transaction-api" - compile "org.ehcache:ehcache" - compile "org.hibernate:hibernate-entitymanager" - compile "org.hibernate:hibernate-envers" - compile "org.hibernate.validator:hibernate-validator" - compile "org.liquibase:liquibase-core" - compile "com.mattbertolini:liquibase-slf4j" - liquibaseRuntime "org.liquibase:liquibase-core" - liquibaseRuntime "org.liquibase.ext:liquibase-hibernate5:${liquibase_hibernate5_version}" - liquibaseRuntime sourceSets.main.compileClasspath - compile "org.springframework.boot:spring-boot-loader-tools" - compile "org.springframework.boot:spring-boot-starter-mail" - compile "org.springframework.boot:spring-boot-starter-logging" - compile "org.springframework.boot:spring-boot-starter-actuator" - compile "org.springframework.boot:spring-boot-starter-aop" - compile "org.springframework.boot:spring-boot-starter-data-jpa" - compile "org.springframework.boot:spring-boot-starter-security" - compile ("org.springframework.boot:spring-boot-starter-web") { - exclude module: 'spring-boot-starter-tomcat' - } - compile "org.springframework.boot:spring-boot-starter-undertow" - compile "org.springframework.boot:spring-boot-starter-thymeleaf" - compile "org.zalando:problem-spring-web:0.24.0-RC.0" - compile "org.springframework.boot:spring-boot-starter-cloud-connectors" - compile "org.springframework.security:spring-security-config" - compile "org.springframework.security:spring-security-data" - compile "org.springframework.security:spring-security-web" - compile "io.jsonwebtoken:jjwt-api" - runtime "io.jsonwebtoken:jjwt-impl" - runtime "io.jsonwebtoken:jjwt-jackson" - compile ("io.springfox:springfox-swagger2") { - exclude module: 'mapstruct' - } - compile "io.springfox:springfox-bean-validators" - compile "org.postgresql:postgresql" - liquibaseRuntime "org.postgresql:postgresql" - compile "org.mapstruct:mapstruct-jdk8:${mapstruct_version}" - annotationProcessor "org.mapstruct:mapstruct-processor:${mapstruct_version}" - annotationProcessor "org.hibernate:hibernate-jpamodelgen" - annotationProcessor ("org.springframework.boot:spring-boot-configuration-processor") { - exclude group: 'com.vaadin.external.google', module: 'android-json' - } - testCompile "com.jayway.jsonpath:json-path" - testCompile "io.cucumber:cucumber-junit" - testCompile "io.cucumber:cucumber-spring" - testCompile ("org.springframework.boot:spring-boot-starter-test") { - exclude group: 'com.vaadin.external.google', module: 'android-json' - } - testCompile "org.springframework.security:spring-security-test" - testCompile "org.springframework.boot:spring-boot-test" - testCompile "org.assertj:assertj-core" - testCompile "junit:junit" - testCompile "org.mockito:mockito-core" - testCompile "com.mattbertolini:liquibase-slf4j" - testCompile "org.hamcrest:hamcrest-library" - testCompile "com.h2database:h2" - liquibaseRuntime "com.h2database:h2" - //jhipster-needle-gradle-dependency - JHipster will add additional dependencies here -} - -task cleanResources(type: Delete) { - delete 'build/resources' -} - -wrapper { - gradleVersion = '4.10.2' -} - -task stage(dependsOn: 'bootWar') { -} - -if (project.hasProperty('nodeInstall')) { - node { - version = "${node_version}" - npmVersion = "${npm_version}" - yarnVersion = "${yarn_version}" - download = false - } -} diff --git a/cfg/spotless/eclipse_formatter.xml b/cfg/spotless/eclipse_formatter.xml deleted file mode 100644 index 4a564e15..00000000 --- a/cfg/spotless/eclipse_formatter.xml +++ /dev/null @@ -1,315 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cfg/spotless/hsadminng.importorder b/cfg/spotless/hsadminng.importorder deleted file mode 100644 index da8e19e5..00000000 --- a/cfg/spotless/hsadminng.importorder +++ /dev/null @@ -1,8 +0,0 @@ -#Organize Import Order -6=javax -5=java -4=org -3=com -2= -1=org.hostsharing -0=\# diff --git a/cfg/spotless/javascript_formatters.xml b/cfg/spotless/javascript_formatters.xml deleted file mode 100755 index 08bdd71d..00000000 --- a/cfg/spotless/javascript_formatters.xml +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 7aaae4ac..00000000 --- a/gradle.properties +++ /dev/null @@ -1,53 +0,0 @@ -rootProject.name=hsadmin-ng -profile=dev - -# Build properties -node_version=10.15.3 -npm_version=6.4.1 -yarn_version=1.13.0 - -# Dependency versions -jhipster_dependencies_version=2.1.1 -# The spring-boot version should match the one managed by -# https://mvnrepository.com/artifact/io.github.jhipster/jhipster-dependencies/${jhipster_dependencies_version} -spring_boot_version=2.0.8.RELEASE -# The hibernate version should match the one managed by -# https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies/${spring-boot.version} --> -hibernate_version=5.2.17.Final -mapstruct_version=1.2.0.Final - -liquibase_hibernate5_version=3.6 -liquibaseTaskPrefix=liquibase - -# jhipster-needle-gradle-property - JHipster will add additional properties here - -## below are some of the gradle performance improvement settings that can be used as required, these are not enabled by default - -## The Gradle daemon aims to improve the startup and execution time of Gradle. -## The daemon is enabled by default in Gradle 3+ setting this to false will disable this. -## TODO: disable daemon on CI, since builds should be clean and reliable on servers -## https://docs.gradle.org/current/userguide/gradle_daemon.html#sec:ways_to_disable_gradle_daemon -## un comment the below line to disable the daemon - -#org.gradle.daemon=false - -## Specifies the JVM arguments used for the daemon process. -## The setting is particularly useful for tweaking memory settings. -## Default value: -Xmx1024m -XX:MaxPermSize=256m -## un comment the below line to override the daemon defaults - -#org.gradle.jvmargs=-Xmx1024m -XX:MaxPermSize=256m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 - -## When configured, Gradle will run in incubating parallel mode. -## This option should only be used with decoupled projects. More details, visit -## http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -## un comment the below line to enable parallel mode - -#org.gradle.parallel=true - -## Enables new incubating mode that makes Gradle selective when configuring projects. -## Only relevant projects are configured which results in faster builds for large multi-projects. -## http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:configuration_on_demand -## un comment the below line to enable the selective mode - -#org.gradle.configureondemand=true diff --git a/gradle/docker.gradle b/gradle/docker.gradle deleted file mode 100644 index a9d40315..00000000 --- a/gradle/docker.gradle +++ /dev/null @@ -1,35 +0,0 @@ -buildscript { - repositories { - gradlePluginPortal() - } - dependencies { - classpath "gradle.plugin.com.google.cloud.tools:jib-gradle-plugin:0.9.11" - } -} - -apply plugin: com.google.cloud.tools.jib.gradle.JibPlugin - -jib { - from { - image = 'openjdk:8-jre-alpine' - } - to { - image = 'hsadminng:latest' - } - container { - entrypoint = ['sh', '-c', 'chmod +x /entrypoint.sh && sync && /entrypoint.sh'] - ports = ['8080'] - environment = [ - SPRING_OUTPUT_ANSI_ENABLED: 'ALWAYS', - JHIPSTER_SLEEP: '0' - ] - useCurrentTimestamp = true - } -} - -task copyWwwIntoStatic (type: Copy) { - from 'build/www/' - into 'build/resources/main/static' -} - -jibDockerBuild.dependsOn copyWwwIntoStatic diff --git a/gradle/profile_dev.gradle b/gradle/profile_dev.gradle deleted file mode 100644 index d19d8f37..00000000 --- a/gradle/profile_dev.gradle +++ /dev/null @@ -1,72 +0,0 @@ -import org.gradle.internal.os.OperatingSystem - -apply plugin: 'org.springframework.boot' -apply plugin: 'com.moowork.node' - -dependencies { - compile "org.springframework.boot:spring-boot-devtools" - compile "com.h2database:h2" -} - -def profiles = ""; -if (project.hasProperty('pgsql')) { - profiles += 'pgsql' -} else if (project.hasProperty('h2file')) { - profiles += 'h2file' -} else { - profiles += 'h2mem' -} -if (project.hasProperty('no-liquibase')) { - profiles += ',no-liquibase' -} -if (project.hasProperty('tls')) { - profiles += ',tls' -} - -println 'activating profiles: ' + profiles - -springBoot { - buildInfo { - properties { - time = null - } - } -} - -bootRun { - args = [] -} - -task webpackBuildDev(type: NpmTask) { - inputs.files(fileTree('src/main/webapp/')) - - def webpackDevFiles = fileTree('webpack//') - webpackDevFiles.exclude('webpack.prod.js') - inputs.files(webpackDevFiles) - - outputs.files(fileTree("build/www/")) - - dependsOn npmInstall - - args = ["run", "webpack:build"] -} - -task copyIntoStatic (type: Copy) { - from 'build/www/' - into 'build/resources/main/static' -} - -processResources { - filesMatching('**/application.yml') { - filter { - it.replace('#project.version#', version) - } - filter { - it.replace('#spring.profiles.active#', profiles) - } - } -} - -processResources.dependsOn webpackBuildDev -copyIntoStatic.dependsOn processResources -bootJar.dependsOn copyIntoStatic diff --git a/gradle/profile_prod.gradle b/gradle/profile_prod.gradle deleted file mode 100644 index edf46392..00000000 --- a/gradle/profile_prod.gradle +++ /dev/null @@ -1,63 +0,0 @@ -apply plugin: 'org.springframework.boot' -apply plugin: 'com.gorylenko.gradle-git-properties' -apply plugin: 'com.moowork.node' - -dependencies { - testCompile "com.h2database:h2" -} - -def profiles = 'prod' -if (project.hasProperty('no-liquibase')) { - profiles += ',no-liquibase' -} - -if (project.hasProperty('swagger')) { - profiles += ',swagger' -} - -springBoot { - buildInfo() -} - -bootRun { - args = [] -} - -task webpack_test(type: NpmTask, dependsOn: 'npm_install') { - args = ["run", "webpack:test"] -} - -task webpack(type: NpmTask, dependsOn: 'npm_install') { - args = ["run", "webpack:prod"] -} - -task copyIntoStatic (type: Copy) { - from 'build/www/' - into 'build/resources/main/static' -} - -processResources { - filesMatching('**/application.yml') { - filter { - it.replace('#project.version#', version) - } - filter { - it.replace('#spring.profiles.active#', profiles) - } - } -} - -generateGitProperties { - onlyIf { - !source.isEmpty() - } -} - -gitProperties { - keys = ['git.branch', 'git.commit.id.abbrev', 'git.commit.id.describe'] -} - -test.dependsOn webpack_test -processResources.dependsOn webpack -copyIntoStatic.dependsOn processResources -bootJar.dependsOn copyIntoStatic diff --git a/gradle/sonar.gradle b/gradle/sonar.gradle deleted file mode 100644 index 27a11c6f..00000000 --- a/gradle/sonar.gradle +++ /dev/null @@ -1,47 +0,0 @@ -apply plugin: "org.sonarqube" -apply plugin: 'jacoco' - -jacoco { - toolVersion = '0.8.2' -} - -jacocoTestReport { - reports { - xml.enabled true - } -} - -sonarqube { - properties { - property "sonar.host.url", "http://localhost:9001" - property "sonar.exclusions", "src/main/webapp/content/**/*.*,src/main/webapp/i18n/*.js, build/www/**/*.*" - - property "sonar.issue.ignore.multicriteria", "S3437,S4502,S4684,UndocumentedApi,BoldAndItalicTagsCheck" - - // Rule https://sonarcloud.io/coding_rules?open=Web%3ABoldAndItalicTagsCheck&rule_key=Web%3ABoldAndItalicTagsCheck is ignored. Even if we agree that using the "i" tag is an awful practice, this is what is recommended by http://fontawesome.io/examples/ - property "sonar.issue.ignore.multicriteria.BoldAndItalicTagsCheck.resourceKey", ">src/main/webapp/app/**/*.*" - property "sonar.issue.ignore.multicriteria.BoldAndItalicTagsCheck.ruleKey", "Web:BoldAndItalicTagsCheck" - - // Rule https://sonarcloud.io/coding_rules?open=squid%3AS3437&rule_key=squid%3AS3437 is ignored, as a JPA-managed field cannot be transient - property "sonar.issue.ignore.multicriteria.S3437.resourceKey", "src/main/java/**/*" - property "sonar.issue.ignore.multicriteria.S3437.ruleKey", "squid:S3437" - - // Rule https://sonarcloud.io/coding_rules?open=squid%3AUndocumentedApi&rule_key=squid%3AUndocumentedApi is ignored, as we want to follow "clean code" guidelines and classes, methods and arguments names should be self-explanatory - property "sonar.issue.ignore.multicriteria.UndocumentedApi.resourceKey", "src/main/java/**/*" - property "sonar.issue.ignore.multicriteria.UndocumentedApi.ruleKey", "squid:UndocumentedApi" - // Rule https://sonarcloud.io/coding_rules?open=squid%3AS4502&rule_key=squid%3AS4502 is ignored, as for JWT tokens we are not subject to CSRF attack - property "sonar.issue.ignore.multicriteria.S4502.resourceKey", "src/main/java/**/*" - property "sonar.issue.ignore.multicriteria.S4502.ruleKey", "squid:S4502" - - // Rule https://sonarcloud.io/coding_rules?open=squid%3AS4684&rule_key=squid%3AS4684 - property "sonar.issue.ignore.multicriteria.S4684.resourceKey", "src/main/java/**/*" - property "sonar.issue.ignore.multicriteria.S4684.ruleKey", "squid:S4684" - - property "sonar.jacoco.reportPaths", "${project.buildDir}/jacoco/test.exec" - property "sonar.java.codeCoveragePlugin", "jacoco" - property "sonar.typescript.lcov.reportPaths", "${project.buildDir}/test-results/lcov.info" - property "sonar.junit.reportPaths", "${project.buildDir}/test-results" - property "sonar.sources", "${project.projectDir}/src/main/" - property "sonar.tests", "${project.projectDir}/src/test/" - } -} diff --git a/gradle/swagger.gradle b/gradle/swagger.gradle deleted file mode 100644 index fbb0c593..00000000 --- a/gradle/swagger.gradle +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Plugin that provides API-first development using OpenAPI-generator to - * generate Spring-MVC endpoint stubs at compile time from an OpenAPI definition file - */ -apply plugin: 'org.openapi.generator' - -openApiGenerate { - generatorName = "spring" - inputSpec = "$rootDir/src/main/resources/swagger/api.yml".toString() - outputDir = "$buildDir/openapi".toString() - apiPackage = "org.hostsharing.hsadminng.web.api" - modelPackage = "org.hostsharing.hsadminng.web.api.model" - apiFilesConstrainedTo = [""] - modelFilesConstrainedTo = [""] - supportingFilesConstrainedTo = ["ApiUtil.java"] - configOptions = [delegatePattern: "true"] - validateSpec = true -} - -sourceSets { - main { - java { - srcDir file("${project.buildDir.path}/openapi/src/main/java") - } - } -} - -compileJava.dependsOn("openApiGenerate") diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 0d4a9516871afd710a9d84d89e31ba77745607bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54413 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 10affbb8..00000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Sun Apr 28 06:46:48 CEST 2019 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/gradle/zipkin.gradle b/gradle/zipkin.gradle deleted file mode 100644 index 21d1b293..00000000 --- a/gradle/zipkin.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - compile "org.springframework.cloud:spring-cloud-starter-zipkin" -} diff --git a/gradlew b/gradlew deleted file mode 100755 index cccdd3d5..00000000 --- a/gradlew +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env sh - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index f9553162..00000000 --- a/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index b13a9116..00000000 --- a/package-lock.json +++ /dev/null @@ -1,18180 +0,0 @@ -{ - "name": "hsadmin-ng", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.1.tgz", - "integrity": "sha512-QDmIbqde75ZZSEFbw6Q6kQWq4cY6C7D67yujXw6XTyubDNAs1tyXJyxTIB8vjSlEKwRizTTDd/B0ZXVcke3Mvw==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.1", - "rxjs": "6.3.3" - }, - "dependencies": { - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular-devkit/core": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.1.tgz", - "integrity": "sha512-56XDWWfIzOAkEk69lBLgmCYybPUA4yjunhmMlCk7vVdb7gbQUyzNjFD04Uj0GjlejatAQ5F76tRwygD9C+3RXQ==", - "dev": true, - "requires": { - "ajv": "6.7.0", - "chokidar": "2.0.4", - "fast-json-stable-stringify": "2.0.0", - "rxjs": "6.3.3", - "source-map": "0.7.3" - }, - "dependencies": { - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular-devkit/schematics": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.1.tgz", - "integrity": "sha512-cd7usiasfSgw75INz72/VssrLr9tiVRYfo1TEdvr9ww0GuQbuQpB33xbV8W135eAV8+wzQ3Ce8ohaDHibvj6Yg==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.1", - "rxjs": "6.3.3" - }, - "dependencies": { - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular/cli": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.3.1.tgz", - "integrity": "sha512-8EvXYRhTqTaTk5PKv7VZxIWJiyG51R9RC9gtpRFx4bbnurqBHdEUxGMmaRsGT8QDbfvVsWnuakE0eeW1CrfZAQ==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.13.1", - "@angular-devkit/core": "7.3.1", - "@angular-devkit/schematics": "7.3.1", - "@schematics/angular": "7.3.1", - "@schematics/update": "0.13.1", - "@yarnpkg/lockfile": "1.1.0", - "ini": "1.3.5", - "inquirer": "6.2.1", - "npm-package-arg": "6.1.0", - "opn": "5.4.0", - "pacote": "9.4.0", - "semver": "5.6.0", - "symbol-observable": "1.2.0" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "inquirer": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz", - "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.0", - "figures": "^2.0.0", - "lodash": "^4.17.10", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.1.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.0.0", - "through": "^2.3.6" - } - }, - "opn": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", - "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@angular/common": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.4.tgz", - "integrity": "sha512-3/i8RtnLTx/90gJHk5maE8zwsSiHgHvLItaa0qVfNlWiU0eCId/PL6TgDkut5vN9SQYL0oxhxFaVd35HmwsmuQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/compiler": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.4.tgz", - "integrity": "sha512-+zyMzPCL45ePEV9nrnYJvhAVgp2Y19bDaq0f0YdZAqAjgDqHzXGGR6wX8GueyJWmUYWx5vwK6Apla4HwDrYA1w==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/compiler-cli": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.4.tgz", - "integrity": "sha512-UhLosSeuwFIfaGqGcYOh9WSOuzEpeuhIRAOt81MeqOQEqkoreUjfxrQq8XWNkdqsPZHtiptF5ZwXlMBxlj9jJg==", - "dev": true, - "requires": { - "canonical-path": "1.0.0", - "chokidar": "^1.4.2", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.7.2", - "magic-string": "^0.25.0", - "minimist": "^1.2.0", - "reflect-metadata": "^0.1.2", - "shelljs": "^0.8.1", - "source-map": "^0.6.1", - "tslib": "^1.9.0", - "yargs": "9.0.1" - }, - "dependencies": { - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@angular/core": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.4.tgz", - "integrity": "sha512-kfAxhIxl89PmB7y81FR/RAv0yWRFcEYxEnTwV+o8jKGfemAXtQ0g/Vh+lJR0SD/TBgFilMxotN1mhwH4A8GShw==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/forms": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.4.tgz", - "integrity": "sha512-DAtOrdlTRsgvmZrsvczCAkY8dhTwZb5DXBmPuSXh0UR9lvEiCgNHGbwEiIiIkAHpw1wSeXZrq0qyy/oJRvf18g==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/platform-browser": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.4.tgz", - "integrity": "sha512-Klt8aKR5SP9bqfMfpSY5vQOY7AQEs8JGuZOk5Bfc2dUtYT2IEIvK2IqO8v2rcFRVO13HOPUxl328efyHqLgI7g==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/platform-browser-dynamic": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.4.tgz", - "integrity": "sha512-J/xWlmaYOPUoCHZ5TiIRiyYa4uRMtCz3aGdBfY8k/NWtNo8SCYaS3aut7Sk4RS5rK8aAVi+aYFlY5YOrlW+Hbg==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/router": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.4.tgz", - "integrity": "sha512-T8Uqf2H1SV1MQI38WwYJ4aa+4NNnvlp2Tp/rkfg6tKcp/cLkKqE6OOfiy9lmW+i/624v8tMgYoBMOUNBjAG23g==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/core": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.2.2.tgz", - "integrity": "sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.2.2", - "@babel/helpers": "^7.2.0", - "@babel/parser": "^7.2.2", - "@babel/template": "^7.2.2", - "@babel/traverse": "^7.2.2", - "@babel/types": "^7.2.2", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.10", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.2.tgz", - "integrity": "sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg==", - "dev": true, - "requires": { - "@babel/types": "^7.2.2", - "jsesc": "^2.5.1", - "lodash": "^4.17.10", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", - "dev": true - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helpers": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.2.0.tgz", - "integrity": "sha512-Fr07N+ea0dMcMN8nFpuK6dUIT7/ivt9yKQdEEnjVS83tG2pHwPi03gYmk/tyuwONnZ+sY+GFFPlWGgCtW1hF9A==", - "dev": true, - "requires": { - "@babel/template": "^7.1.2", - "@babel/traverse": "^7.1.5", - "@babel/types": "^7.2.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.2.tgz", - "integrity": "sha512-UNTmQ5cSLDeBGBl+s7JeowkqIHgmFAGBnLDdIzFmUNSuS5JF0XBcN59jsh/vJO/YjfsBqMxhMjoFGmNExmf0FA==", - "dev": true - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/runtime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", - "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.12.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", - "dev": true - } - } - }, - "@babel/template": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", - "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.2.2", - "@babel/types": "^7.2.2" - } - }, - "@babel/traverse": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.2.tgz", - "integrity": "sha512-E5Bn9FSwHpSkUhthw/XEuvFZxIgrqb9M8cX8j5EUQtrUG5DQUy6bFyl7G7iQ1D1Czudor+xkmp81JbLVVM0Sjg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.2.2", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.2.2", - "@babel/types": "^7.2.2", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.10" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", - "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "@fortawesome/angular-fontawesome": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.3.0.tgz", - "integrity": "sha512-wXvyPI7GidoNiqeMz2re9iemUMFH4zBmuv94CfXlaanQ8+kMP/fYs/k69PLVN1KsebQY4kRA9GHmc1U1ndBkJg==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@fortawesome/fontawesome-common-types": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.17.tgz", - "integrity": "sha512-DEYsEb/iiGVoMPQGjhG2uOylLVuMzTxOxysClkabZ5n80Q3oFDWGnshCLKvOvKoeClsgmKhWVrnnqvsMI1cAbw==" - }, - "@fortawesome/fontawesome-svg-core": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.14.tgz", - "integrity": "sha512-T1qCqkwm9PuvK53J64D1ovfrOTa1kG+SrHNj5cFst/rrskhCnbxpRdbqFIdc/thmXC0ebBX8nOUyja2/mrxe4g==", - "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.14" - } - }, - "@fortawesome/free-solid-svg-icons": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.7.1.tgz", - "integrity": "sha512-5V/Q+JoPzuiIHW2JwmZGvE9bHguvNJKa7611DPo51uIvYv9LweX/SnDF+HC23X2W5T3myHhnGi+EZJTmidAmyg==", - "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.14" - } - }, - "@iamstarkov/listr-update-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz", - "integrity": "sha512-IJyxQWsYDEkf8C8QthBn5N8tIUR9V9je6j3sMIpAkonaadjbvxmRC6RAhpa3RKxndhNnU2M6iNbtJwd7usQYIA==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - } - } - } - }, - "@jest/console": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.3.0.tgz", - "integrity": "sha512-NaCty/OOei6rSDcbPdMiCbYCI0KGFGPgGO6B09lwWt5QTxnkuhKYET9El5u5z1GAcSxkQmSMtM63e24YabCWqA==", - "dev": true, - "requires": { - "@jest/source-map": "^24.3.0", - "@types/node": "*", - "chalk": "^2.0.1", - "slash": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@jest/core": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.5.0.tgz", - "integrity": "sha512-RDZArRzAs51YS7dXG1pbXbWGxK53rvUu8mCDYsgqqqQ6uSOaTjcVyBl2Jce0exT2rSLk38ca7az7t2f3b0/oYQ==", - "dev": true, - "requires": { - "@jest/console": "^24.3.0", - "@jest/reporters": "^24.5.0", - "@jest/test-result": "^24.5.0", - "@jest/transform": "^24.5.0", - "@jest/types": "^24.5.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.5.0", - "jest-config": "^24.5.0", - "jest-haste-map": "^24.5.0", - "jest-message-util": "^24.5.0", - "jest-regex-util": "^24.3.0", - "jest-resolve-dependencies": "^24.5.0", - "jest-runner": "^24.5.0", - "jest-runtime": "^24.5.0", - "jest-snapshot": "^24.5.0", - "jest-util": "^24.5.0", - "jest-validate": "^24.5.0", - "jest-watcher": "^24.5.0", - "micromatch": "^3.1.10", - "p-each-series": "^1.0.0", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "rimraf": "^2.5.4", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@jest/environment": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.5.0.tgz", - "integrity": "sha512-tzUHR9SHjMXwM8QmfHb/EJNbF0fjbH4ieefJBvtwO8YErLTrecc1ROj0uo2VnIT6SlpEGZnvdCK6VgKYBo8LsA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^24.5.0", - "@jest/transform": "^24.5.0", - "@jest/types": "^24.5.0", - "@types/node": "*", - "jest-mock": "^24.5.0" - } - }, - "@jest/fake-timers": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.5.0.tgz", - "integrity": "sha512-i59KVt3QBz9d+4Qr4QxsKgsIg+NjfuCjSOWj3RQhjF5JNy+eVJDhANQ4WzulzNCHd72srMAykwtRn5NYDGVraw==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0", - "@types/node": "*", - "jest-message-util": "^24.5.0", - "jest-mock": "^24.5.0" - } - }, - "@jest/reporters": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.5.0.tgz", - "integrity": "sha512-vfpceiaKtGgnuC3ss5czWOihKOUSyjJA4M4udm6nH8xgqsuQYcyDCi4nMMcBKsHXWgz9/V5G7iisnZGfOh1w6Q==", - "dev": true, - "requires": { - "@jest/environment": "^24.5.0", - "@jest/test-result": "^24.5.0", - "@jest/transform": "^24.5.0", - "@jest/types": "^24.5.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.2", - "istanbul-api": "^2.1.1", - "istanbul-lib-coverage": "^2.0.2", - "istanbul-lib-instrument": "^3.0.1", - "istanbul-lib-source-maps": "^3.0.1", - "jest-haste-map": "^24.5.0", - "jest-resolve": "^24.5.0", - "jest-runtime": "^24.5.0", - "jest-util": "^24.5.0", - "jest-worker": "^24.4.0", - "node-notifier": "^5.2.1", - "slash": "^2.0.0", - "source-map": "^0.6.0", - "string-length": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@jest/source-map": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", - "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - }, - "dependencies": { - "callsites": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", - "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@jest/test-result": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.5.0.tgz", - "integrity": "sha512-u66j2vBfa8Bli1+o3rCaVnVYa9O8CAFZeqiqLVhnarXtreSXG33YQ6vNYBogT7+nYiFNOohTU21BKiHlgmxD5A==", - "dev": true, - "requires": { - "@jest/console": "^24.3.0", - "@jest/types": "^24.5.0", - "@types/istanbul-lib-coverage": "^1.1.0" - } - }, - "@jest/transform": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.5.0.tgz", - "integrity": "sha512-XSsDz1gdR/QMmB8UCKlweAReQsZrD/DK7FuDlNo/pE8EcKMrfi2kqLRk8h8Gy/PDzgqJj64jNEzOce9pR8oj1w==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.5.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.5.0", - "jest-regex-util": "^24.3.0", - "jest-util": "^24.5.0", - "micromatch": "^3.1.10", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "write-file-atomic": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", - "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - } - } - }, - "@jest/types": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.5.0.tgz", - "integrity": "sha512-kN7RFzNMf2R8UDadPOl6ReyI+MT8xfqRuAnuVL+i4gwjv/zubdDK+EDeLHYwq1j0CSSR2W/MmgaRlMZJzXdmVA==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^1.1.0", - "@types/yargs": "^12.0.9" - } - }, - "@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", - "dev": true, - "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" - } - }, - "@ng-bootstrap/ng-bootstrap": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-4.0.2.tgz", - "integrity": "sha512-SBsN8ORvj/WXpZGSyR2+CRkg6GCtax5+fsLKt9ImHKUVWwePVqRxiGlnxXqwNPHQ46vOdd7nDN9cwE7dfbGaAQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@ngtools/webpack": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.1.tgz", - "integrity": "sha512-EGQRjgDf5XP+Fm1MdZNRFiPd9e1vhl11BhjkwqkAsewic4eoz6fqXfj/Osz1hQy8xU+2dPPf/byQ/+nY3E02Zg==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.1", - "enhanced-resolve": "4.1.0", - "rxjs": "6.3.3", - "tree-kill": "1.2.1", - "webpack-sources": "1.3.0" - }, - "dependencies": { - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@ngx-translate/core": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-11.0.1.tgz", - "integrity": "sha512-nBCa1ZD9fAUY/3eskP3Lql2fNg8OMrYIej1/5GRsfcutx9tG/5fZLCv9m6UCw1aS+u4uK/vXjv1ctG/FdMvaWg==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@ngx-translate/http-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-4.0.0.tgz", - "integrity": "sha512-x8LumqydWD7eX9yQTAVeoCM9gFUIGVTUjZqbxdAUavAA3qVnk9wCQux7iHLPXpydl8vyQmLoPQR+fFU+DUDOMA==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", - "dev": true - }, - "@samverschueren/stream-to-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", - "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", - "dev": true, - "requires": { - "any-observable": "^0.3.0" - } - }, - "@schematics/angular": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.1.tgz", - "integrity": "sha512-0Ne8APPlTAjKg5CSZqluwCuW/5yPjr3ALCWzqwPxN0suE745usThtasBmqrjw0RMIt8nRqRgtg54Z7lCPO9ZFg==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.1", - "@angular-devkit/schematics": "7.3.1", - "typescript": "3.2.4" - } - }, - "@schematics/update": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.13.1.tgz", - "integrity": "sha512-EHOqolT/d/jRGuVTCUESLpk8JNpuaPlsVHfeK7Kdp/t0wSEnmtOelZX4+leS25lGXDaDUF3138ntjrZR4n6bGw==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.1", - "@angular-devkit/schematics": "7.3.1", - "@yarnpkg/lockfile": "1.1.0", - "ini": "1.3.5", - "pacote": "9.4.0", - "rxjs": "6.3.3", - "semver": "5.6.0", - "semver-intersect": "1.4.0" - }, - "dependencies": { - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@types/babel__core": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.0.tgz", - "integrity": "sha512-wJTeJRt7BToFx3USrCDs2BhEi4ijBInTQjOIukj6a/5tEkwpFMVZ+1ppgmE+Q/FQyc5P/VWUbx7I9NELrKruHA==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", - "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz", - "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/istanbul-lib-coverage": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz", - "integrity": "sha512-ohkhb9LehJy+PA40rDtGAji61NCgdtKLAlFoYp4cnuuQEswwdK3vz9SOIkkyc3wrk8dzjphQApNs56yyXLStaQ==", - "dev": true - }, - "@types/jest": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.0.tgz", - "integrity": "sha512-kOafJnUTnMd7/OfEO/x3I47EHswNjn+dbz9qk3mtonr1RvKT+1FGVxnxAx08I9K8Tl7j9hpoJRE7OCf+t10fng==", - "dev": true - }, - "@types/node": { - "version": "10.12.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.24.tgz", - "integrity": "sha512-GWWbvt+z9G5otRBW8rssOFgRY87J9N/qbhqfjMZ+gUuL6zoL+Hm6gP/8qQBG4jjimqdaNLCehcVapZ/Fs2WjCQ==", - "dev": true - }, - "@types/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz", - "integrity": "sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==", - "dev": true - }, - "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true - }, - "@types/yargs": { - "version": "12.0.10", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.10.tgz", - "integrity": "sha512-WsVzTPshvCSbHThUduGGxbmnwcpkgSctHGHTqzWyFg4lYAuV5qXlyFPOsP3OWqCINfmg/8VXP+zJaa4OxEsBQQ==", - "dev": true - }, - "@webassemblyjs/ast": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", - "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==", - "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/wast-parser": "1.7.11" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz", - "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz", - "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz", - "integrity": "sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz", - "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.7.11" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz", - "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz", - "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==", - "dev": true - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz", - "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz", - "integrity": "sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz", - "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz", - "integrity": "sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.1" - } - }, - "@webassemblyjs/utf8": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz", - "integrity": "sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz", - "integrity": "sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/helper-wasm-section": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11", - "@webassemblyjs/wasm-opt": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11", - "@webassemblyjs/wast-printer": "1.7.11" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz", - "integrity": "sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/ieee754": "1.7.11", - "@webassemblyjs/leb128": "1.7.11", - "@webassemblyjs/utf8": "1.7.11" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz", - "integrity": "sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz", - "integrity": "sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-api-error": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/ieee754": "1.7.11", - "@webassemblyjs/leb128": "1.7.11", - "@webassemblyjs/utf8": "1.7.11" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz", - "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/floating-point-hex-parser": "1.7.11", - "@webassemblyjs/helper-api-error": "1.7.11", - "@webassemblyjs/helper-code-frame": "1.7.11", - "@webassemblyjs/helper-fsm": "1.7.11", - "@xtuc/long": "4.2.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz", - "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/wast-parser": "1.7.11", - "@xtuc/long": "4.2.1" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", - "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", - "dev": true - }, - "@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", - "dev": true - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "dev": true, - "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", - "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", - "dev": true - }, - "acorn-globals": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz", - "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==", - "dev": true, - "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - } - } - }, - "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", - "dev": true - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "agentkeepalive": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", - "dev": true, - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "ajv": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz", - "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "ajv-keywords": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", - "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", - "dev": true - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "angular-router-loader": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/angular-router-loader/-/angular-router-loader-0.8.5.tgz", - "integrity": "sha512-8wggCTKGgzB1o8co3Wvj+p9pKN7T7q3C477lEz3NLjvPVzUti8rv9i45Di+4aO/k+HvzGh3s8QdNlXU2Bl4avQ==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2" - } - }, - "angular2-template-loader": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/angular2-template-loader/-/angular2-template-loader-0.6.2.tgz", - "integrity": "sha1-wNROkP/w+sleiyPwQ6zaf9HFHXw=", - "dev": true, - "requires": { - "loader-utils": "^0.2.15" - }, - "dependencies": { - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - } - } - }, - "ansi": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", - "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=", - "dev": true - }, - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true - }, - "ansi-cyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", - "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", - "dev": true, - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-red": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", - "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", - "dev": true, - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true - }, - "any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", - "dev": true - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "app-root-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz", - "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=", - "dev": true - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - } - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", - "dev": true - }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "ast-types": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", - "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "async-each-series": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", - "integrity": "sha1-dhfBkXQB/Yykooqtzj266Yr+tDI=", - "dev": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "autoprefixer": { - "version": "9.4.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.7.tgz", - "integrity": "sha512-qS5wW6aXHkm53Y4z73tFGsUhmZu4aMPV9iHXYlF0c/wxjknXNHuj/1cIQb+6YH692DbJGGWcckAXX+VxKvahMA==", - "dev": true, - "requires": { - "browserslist": "^4.4.1", - "caniuse-lite": "^1.0.30000932", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.14", - "postcss-value-parser": "^3.3.1" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true - }, - "axios": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz", - "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=", - "dev": true, - "requires": { - "follow-redirects": "^1.2.5", - "is-buffer": "^1.1.5" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, - "babel-extract-comments": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz", - "integrity": "sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ==", - "dev": true, - "requires": { - "babylon": "^6.18.0" - } - }, - "babel-jest": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.5.0.tgz", - "integrity": "sha512-0fKCXyRwxFTJL0UXDJiT2xYxO9Lu2vBd9n+cC+eDjESzcVG3s2DRGAxbzJX21fceB1WYoBjAh8pQ83dKcl003g==", - "dev": true, - "requires": { - "@jest/transform": "^24.5.0", - "@jest/types": "^24.5.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.3.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "babel-plugin-istanbul": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz", - "integrity": "sha512-RNNVv2lsHAXJQsEJ5jonQwrJVWK8AcZpG1oxhnjCUaAjL7xahYLANhPUZbzEQHjKy1NMYUwn+0NPKQc8iSY4xQ==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.0.0", - "test-exclude": "^5.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - } - } - }, - "babel-plugin-jest-hoist": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.3.0.tgz", - "integrity": "sha512-nWh4N1mVH55Tzhx2isvUN5ebM5CDUvIpXPZYMRazQughie/EqGnbR+czzoQlhUmJG9pPJmYDRhvocotb2THl1w==", - "dev": true, - "requires": { - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", - "dev": true - }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", - "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.8.0", - "babel-runtime": "^6.26.0" - } - }, - "babel-polyfill": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz", - "integrity": "sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=", - "requires": { - "babel-runtime": "^6.22.0", - "core-js": "^2.4.0", - "regenerator-runtime": "^0.10.0" - } - }, - "babel-preset-jest": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.3.0.tgz", - "integrity": "sha512-VGTV2QYBa/Kn3WCOKdfS31j9qomaXSgJqi65B6o05/1GsJyj9LVhSljM9ro4S+IBGj/ENhNBuH9bpqzztKAQSw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.3.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - } - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base62": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/base62/-/base62-1.2.8.tgz", - "integrity": "sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA==", - "dev": true - }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true - }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true - }, - "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", - "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", - "dev": true - }, - "binaryextensions": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.2.tgz", - "integrity": "sha512-xVNN69YGDghOqCCtA6FI7avYrr02mTJjOgB0/f1VPD3pJC8QEvjTKWc4epDx8AqxxA75NI0QpVM2gPJXUbE4Tg==", - "dev": true - }, - "blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", - "dev": true - }, - "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "bootstrap": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.2.1.tgz", - "integrity": "sha512-tt/7vIv3Gm2mnd/WeDx36nfGGHleil0Wg8IeB7eMrVkY0fZ5iTaBisSh8oNANc2IBsCc6vCgCNTIM/IEN0+50Q==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-process-hrtime": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", - "dev": true - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "browser-sync": { - "version": "2.26.3", - "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.26.3.tgz", - "integrity": "sha512-VLzpjCA4uXqfzkwqWtMM6hvPm2PNHp2RcmzBXcbi6C9WpkUhhFb8SVAr4CFrCsFxDg+oY6HalOjn8F+egyvhag==", - "dev": true, - "requires": { - "browser-sync-client": "^2.26.2", - "browser-sync-ui": "^2.26.2", - "bs-recipes": "1.3.4", - "bs-snippet-injector": "^2.0.1", - "chokidar": "^2.0.4", - "connect": "3.6.6", - "connect-history-api-fallback": "^1", - "dev-ip": "^1.0.1", - "easy-extender": "^2.3.4", - "eazy-logger": "^3", - "etag": "^1.8.1", - "fresh": "^0.5.2", - "fs-extra": "3.0.1", - "http-proxy": "1.15.2", - "immutable": "^3", - "localtunnel": "1.9.1", - "micromatch": "2.3.11", - "opn": "5.3.0", - "portscanner": "2.1.1", - "qs": "6.2.3", - "raw-body": "^2.3.2", - "resp-modifier": "6.0.2", - "rx": "4.1.0", - "send": "0.16.2", - "serve-index": "1.9.1", - "serve-static": "1.13.2", - "server-destroy": "1.0.1", - "socket.io": "2.1.1", - "ua-parser-js": "0.7.17", - "yargs": "6.4.0" - }, - "dependencies": { - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "opn": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", - "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^1.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.4.0.tgz", - "integrity": "sha1-gW4ahm1VmMzzTlWW3c4i2S2kkNQ=", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "window-size": "^0.2.0", - "y18n": "^3.2.1", - "yargs-parser": "^4.1.0" - } - }, - "yargs-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", - "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", - "dev": true, - "requires": { - "camelcase": "^3.0.0" - } - } - } - }, - "browser-sync-client": { - "version": "2.26.2", - "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.26.2.tgz", - "integrity": "sha512-FEuVJD41fI24HJ30XOT2RyF5WcnEtdJhhTqeyDlnMk/8Ox9MZw109rvk9pdfRWye4soZLe+xcAo9tHSMxvgAdw==", - "dev": true, - "requires": { - "etag": "1.8.1", - "fresh": "0.5.2", - "mitt": "^1.1.3", - "rxjs": "^5.5.6" - }, - "dependencies": { - "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", - "dev": true, - "requires": { - "symbol-observable": "1.0.1" - } - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - } - } - }, - "browser-sync-ui": { - "version": "2.26.2", - "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.26.2.tgz", - "integrity": "sha512-LF7GMWo8ELOE0eAlxuRCfnGQT1ZxKP9flCfGgZdXFc6BwmoqaJHlYe7MmVvykKkXjolRXTz8ztXAKGVqNwJ3EQ==", - "dev": true, - "requires": { - "async-each-series": "0.1.1", - "connect-history-api-fallback": "^1", - "immutable": "^3", - "server-destroy": "1.0.1", - "socket.io-client": "^2.0.4", - "stream-throttle": "^0.1.3" - } - }, - "browser-sync-webpack-plugin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/browser-sync-webpack-plugin/-/browser-sync-webpack-plugin-2.2.2.tgz", - "integrity": "sha512-x92kl8LdBi4dp6YVXYqrSoDkOCOLCeBOrYSY0h9Sk1VcCDSoZC1Vc62eae6TfC2ljN4/L+aYlkzE46kirHzbgA==", - "dev": true, - "requires": { - "lodash": "^4" - } - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.2.tgz", - "integrity": "sha512-ISS/AIAiHERJ3d45Fz0AVYKkgcy+F/eJHzKEvv1j0wwKGKD9T3BrwKr/5g45L+Y4XIK5PlTqefHciRFcfE1Jxg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000939", - "electron-to-chromium": "^1.3.113", - "node-releases": "^1.1.8" - } - }, - "bs-recipes": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", - "integrity": "sha1-DS1NSKcYyMBEdp/cT4lZLci2lYU=", - "dev": true - }, - "bs-snippet-injector": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/bs-snippet-injector/-/bs-snippet-injector-2.0.1.tgz", - "integrity": "sha1-YbU5PxH1JVntEgaTEANDtu2wTdU=", - "dev": true - }, - "bser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cache-loader": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-2.0.1.tgz", - "integrity": "sha512-V99T3FOynmGx26Zom+JrVBytLBsmUCzVG2/4NnUKgvXN4bEV42R1ERl1IyiH/cvFIDA1Ytq2lPZ9tXDSahcQpQ==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.0", - "normalize-path": "^3.0.0", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - } - } - }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", - "dev": true - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true, - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" - } - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30000955", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000955.tgz", - "integrity": "sha512-6AwmIKgqCYfDWWadRkAuZSHMQP4Mmy96xAXEdRBlN/luQhlRYOKgwOlZ9plpCOsVbBuqbTmGqDK3JUM/nlr8CA==", - "dev": true - }, - "canonical-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", - "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" - }, - "chevrotain": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-4.2.0.tgz", - "integrity": "sha512-uiwhNpkudwrk3rHxKKfrvsWNe4SBDjnswbF2FDqDfrqsfYr4gY0Yl1k2m9yPKR0fqfbiIP67EbgOv4e+JP+GGg==", - "dev": true, - "requires": { - "regexp-to-ast": "0.3.5" - } - }, - "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" - } - }, - "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", - "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-table": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", - "dev": true, - "requires": { - "colors": "1.0.3" - }, - "dependencies": { - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - } - } - }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "dev": true, - "requires": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true - }, - "cloneable-readable": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", - "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" - } - }, - "closest-file-data": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/closest-file-data/-/closest-file-data-0.1.4.tgz", - "integrity": "sha1-l1+HwTLymdJKA3W59jyj+4j3Kzo=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dev": true, - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "codelyzer": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.5.0.tgz", - "integrity": "sha512-oO6vCkjqsVrEsmh58oNlnJkRXuA30hF8cdNAQV9DytEalDwyOFRvHMnlKFzmOStNerOmPGZU9GAHnBo4tGvtiQ==", - "dev": true, - "requires": { - "app-root-path": "^2.1.0", - "css-selector-tokenizer": "^0.7.0", - "cssauron": "^1.4.0", - "semver-dsl": "^1.0.1", - "source-map": "^0.5.7", - "sprintf-js": "^1.1.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "dev": true, - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colornames": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", - "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=", - "dev": true - }, - "colors": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", - "dev": true - }, - "colorspace": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.1.tgz", - "integrity": "sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw==", - "dev": true, - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true - }, - "common-tags": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", - "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "commoner": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz", - "integrity": "sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=", - "dev": true, - "requires": { - "commander": "^2.5.0", - "detective": "^4.3.1", - "glob": "^5.0.15", - "graceful-fs": "^4.1.2", - "iconv-lite": "^0.4.5", - "mkdirp": "^0.5.0", - "private": "^0.1.6", - "q": "^1.1.2", - "recast": "^0.11.17" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "compare-versions": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", - "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==", - "dev": true - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "compressible": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", - "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", - "dev": true, - "requires": { - "mime-db": ">= 1.38.0 < 2" - } - }, - "compression": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.14", - "debug": "2.6.9", - "on-headers": "~1.0.1", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "conf": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/conf/-/conf-2.0.0.tgz", - "integrity": "sha512-iCLzBsGFi8S73EANsEJZz0JnJ/e5VZef/kSaxydYZLAvw0rFNAUx5R7K5leC/CXXR2mZfXWhUvcZOO/dM2D5xg==", - "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "env-paths": "^1.0.0", - "make-dir": "^1.0.0", - "pkg-up": "^2.0.0", - "write-file-atomic": "^2.3.0" - } - }, - "connect": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", - "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.0", - "parseurl": "~1.3.2", - "utils-merge": "1.0.1" - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", - "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", - "dev": true, - "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "globby": "^7.1.1", - "is-glob": "^4.0.0", - "loader-utils": "^1.1.0", - "minimatch": "^3.0.4", - "p-limit": "^1.0.0", - "serialize-javascript": "^1.4.0" - }, - "dependencies": { - "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "core-js": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.4.tgz", - "integrity": "sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cosmiconfig": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.7.tgz", - "integrity": "sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA==", - "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - } - } - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-declaration-sorter": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", - "dev": true, - "requires": { - "postcss": "^7.0.1", - "timsort": "^0.3.0" - } - }, - "css-loader": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.0.tgz", - "integrity": "sha512-MoOu+CStsGrSt5K2OeZ89q3Snf+IkxRfAIt9aAKg4piioTrhtP1iEFPu+OVn3Ohz24FO6L+rw9UJxBILiSBw5Q==", - "dev": true, - "requires": { - "icss-utils": "^4.0.0", - "loader-utils": "^1.2.1", - "lodash": "^4.17.11", - "postcss": "^7.0.6", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^2.0.3", - "postcss-modules-scope": "^2.0.0", - "postcss-modules-values": "^2.0.0", - "postcss-value-parser": "^3.3.0", - "schema-utils": "^1.0.0" - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "dev": true - }, - "css-selector-tokenizer": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", - "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", - "dev": true, - "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" - } - }, - "css-tree": { - "version": "1.0.0-alpha.28", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz", - "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==", - "dev": true, - "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "css-unit-converter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz", - "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=", - "dev": true - }, - "css-url-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz", - "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=", - "dev": true - }, - "css-what": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz", - "integrity": "sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ==", - "dev": true - }, - "cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "dev": true, - "requires": { - "through": "X.X.X" - } - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "cssnano": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", - "dev": true, - "requires": { - "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.7", - "is-resolvable": "^1.0.0", - "postcss": "^7.0.0" - } - }, - "cssnano-preset-default": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", - "dev": true, - "requires": { - "css-declaration-sorter": "^4.0.1", - "cssnano-util-raw-cache": "^4.0.1", - "postcss": "^7.0.0", - "postcss-calc": "^7.0.1", - "postcss-colormin": "^4.0.3", - "postcss-convert-values": "^4.0.1", - "postcss-discard-comments": "^4.0.2", - "postcss-discard-duplicates": "^4.0.2", - "postcss-discard-empty": "^4.0.1", - "postcss-discard-overridden": "^4.0.1", - "postcss-merge-longhand": "^4.0.11", - "postcss-merge-rules": "^4.0.3", - "postcss-minify-font-values": "^4.0.2", - "postcss-minify-gradients": "^4.0.2", - "postcss-minify-params": "^4.0.2", - "postcss-minify-selectors": "^4.0.2", - "postcss-normalize-charset": "^4.0.1", - "postcss-normalize-display-values": "^4.0.2", - "postcss-normalize-positions": "^4.0.2", - "postcss-normalize-repeat-style": "^4.0.2", - "postcss-normalize-string": "^4.0.2", - "postcss-normalize-timing-functions": "^4.0.2", - "postcss-normalize-unicode": "^4.0.1", - "postcss-normalize-url": "^4.0.1", - "postcss-normalize-whitespace": "^4.0.2", - "postcss-ordered-values": "^4.1.2", - "postcss-reduce-initial": "^4.0.3", - "postcss-reduce-transforms": "^4.0.2", - "postcss-svgo": "^4.0.2", - "postcss-unique-selectors": "^4.0.1" - } - }, - "cssnano-util-get-arguments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", - "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", - "dev": true - }, - "cssnano-util-get-match": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", - "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", - "dev": true - }, - "cssnano-util-raw-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "cssnano-util-same-parent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", - "dev": true - }, - "csso": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", - "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", - "dev": true, - "requires": { - "css-tree": "1.0.0-alpha.29" - }, - "dependencies": { - "css-tree": { - "version": "1.0.0-alpha.29", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", - "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", - "dev": true, - "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "cssom": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", - "dev": true - }, - "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", - "dev": true, - "requires": { - "cssom": "0.3.x" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "d3": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz", - "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=", - "dev": true - }, - "dargs": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-6.0.0.tgz", - "integrity": "sha512-6lJauzNaI7MiM8EHQWmGj+s3rP5/i1nYs8GAvKrLAx/9dpc9xS/4seFb1ioR39A+kcfu4v3jnEa/EE5qWYnitQ==", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" - }, - "dependencies": { - "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } - } - }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", - "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - } - } - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "default-gateway": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", - "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", - "dev": true, - "requires": { - "execa": "^0.10.0", - "ip-regex": "^2.1.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } - } - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "dependency-graph": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", - "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-conflict": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/detect-conflict/-/detect-conflict-1.0.1.tgz", - "integrity": "sha1-CIZXpmqWHAUBnbfEIwiDsca0F24=", - "dev": true - }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, - "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", - "dev": true - }, - "detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", - "dev": true - }, - "detective": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", - "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", - "dev": true, - "requires": { - "acorn": "^5.2.1", - "defined": "^1.0.0" - } - }, - "dev-ip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", - "integrity": "sha1-p2o+0YVb56ASu4rBbLgPPADcKPA=", - "dev": true - }, - "diagnostics": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", - "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", - "dev": true, - "requires": { - "colorspace": "1.1.x", - "enabled": "1.0.x", - "kuler": "1.0.x" - } - }, - "didyoumean": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz", - "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diff-sequences": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "dev": true, - "requires": { - "path-type": "^3.0.0" - }, - "dependencies": { - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "requires": { - "utila": "~0.4" - } - }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "domhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", - "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "drange": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", - "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", - "dev": true - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "easy-extender": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz", - "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==", - "dev": true, - "requires": { - "lodash": "^4.17.10" - } - }, - "eazy-logger": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-3.0.2.tgz", - "integrity": "sha1-oyWqXlPROiIliJsqxBE7K5Y29Pw=", - "dev": true, - "requires": { - "tfunk": "^3.0.1" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "editions": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/editions/-/editions-2.1.3.tgz", - "integrity": "sha512-xDZyVm0A4nLgMNWVVLJvcwMjI80ShiH/27RyLiCnW1L273TcJIA25C4pwJ33AWV01OX6UriP35Xu+lH4S7HWQw==", - "dev": true, - "requires": { - "errlop": "^1.1.1", - "semver": "^5.6.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "ejs": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", - "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.113", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", - "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", - "dev": true - }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", - "dev": true - }, - "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "enabled": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", - "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", - "dev": true, - "requires": { - "env-variable": "0.0.x" - } - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "engine.io": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - } - } - }, - "engine.io-client": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.2.tgz", - "integrity": "sha512-y0CPINnhMvPuwtqXfsGuWE8BB66+B6wTtCofQDRecMQPYX3MYUZXFNKDhdrSe3EVjgOu4V3rxdeqN/Tr91IgbQ==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~6.1.0", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "engine.io-parser": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, - "env-paths": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", - "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=", - "dev": true - }, - "env-variable": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", - "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==", - "dev": true - }, - "envify": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/envify/-/envify-3.4.1.tgz", - "integrity": "sha1-1xIjKejfFoi6dxsSUBkXyc5cvOg=", - "dev": true, - "requires": { - "jstransform": "^11.0.3", - "through": "~2.3.4" - } - }, - "err-code": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", - "dev": true - }, - "errlop": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/errlop/-/errlop-1.1.1.tgz", - "integrity": "sha512-WX7QjiPHhsny7/PQvrhS5VMizXXKoKCS3udaBp8gjlARdbn+XmK300eKBAAN0hGyRaTCtRpOaxK+xFVPUJ3zkw==", - "dev": true, - "requires": { - "editions": "^2.1.2" - } - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", - "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", - "dev": true, - "requires": { - "string-template": "~0.2.1", - "xtend": "~4.0.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "error-stack-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.2.tgz", - "integrity": "sha512-E1fPutRDdIj/hohG0UpT5mayXNCxXP9d+snxFsPU9X0XgccOumKraa3juDMwTUyi7+Bu5+mCGagjg4IYeNbOdw==", - "dev": true, - "requires": { - "stackframe": "^1.0.4" - } - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", - "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "es6-templates": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", - "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=", - "dev": true, - "requires": { - "recast": "~0.11.12", - "through": "~2.3.6" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", - "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", - "dev": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "eslint-scope": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.2.tgz", - "integrity": "sha512-5q1+B/ogmHl8+paxtOKx38Z8LtWkVGuNt3+GQNErqwLl6ViNp/gdJGMCjZNxZ8j/VYjDNZ2Fo+eQc1TAVPIzbg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "eventemitter3": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", - "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", - "dev": true - }, - "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", - "dev": true - }, - "eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", - "dev": true, - "requires": { - "original": "^1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "exec-sh": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", - "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", - "dev": true - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "^2.1.0" - }, - "dependencies": { - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dev": true, - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "expect": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.5.0.tgz", - "integrity": "sha512-p2Gmc0CLxOgkyA93ySWmHFYHUPFIHG6XZ06l7WArWAsrqYVaVEkOU5NtT5i68KUyGKbkQgDCkiT65bWmdoL6Bw==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.3.0", - "jest-matcher-utils": "^24.5.0", - "jest-message-util": "^24.5.0", - "jest-regex-util": "^24.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - } - } - }, - "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "fast-glob": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.6.tgz", - "integrity": "sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w==", - "dev": true, - "requires": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.1.2", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.3", - "micromatch": "^3.1.10" - } - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fast-safe-stringify": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", - "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==", - "dev": true - }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fb-watchman": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", - "dev": true, - "requires": { - "bser": "^2.0.0" - } - }, - "fbjs": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.6.1.tgz", - "integrity": "sha1-lja3cF9bqWhNRLcveDISVK/IYPc=", - "dev": true, - "requires": { - "core-js": "^1.0.0", - "loose-envify": "^1.0.0", - "promise": "^7.0.3", - "ua-parser-js": "^0.7.9", - "whatwg-fetch": "^0.9.0" - }, - "dependencies": { - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", - "dev": true - } - } - }, - "fecha": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", - "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==", - "dev": true - }, - "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", - "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^1.0.0" - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - } - }, - "filesize": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", - "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", - "dev": true - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" - } - }, - "find-parent-dir": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", - "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "first-chunk-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", - "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "fn-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fn-name/-/fn-name-2.0.1.tgz", - "integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=", - "dev": true - }, - "follow-redirects": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", - "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", - "dev": true, - "requires": { - "debug": "^3.2.6" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "fork-ts-checker-webpack-plugin": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-0.5.2.tgz", - "integrity": "sha512-a5IG+xXyKnpruI0CP/anyRLAoxWtp3lzdG6flxicANnoSzz64b12dJ7ASAVRrI2OaWwZR2JyBaMHFQqInhWhIw==", - "dev": true, - "requires": { - "babel-code-frame": "^6.22.0", - "chalk": "^2.4.1", - "chokidar": "^2.0.4", - "micromatch": "^3.1.10", - "minimatch": "^3.0.4", - "tapable": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "friendly-errors-webpack-plugin": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0.tgz", - "integrity": "sha512-K27M3VK30wVoOarP651zDmb93R9zF28usW4ocaK3mfQeIEI5BPht/EzZs5E8QLLwbLRJQMwscAjDxYPb1FuNiw==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "error-stack-parser": "^2.0.0", - "string-width": "^2.0.0" - } - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", - "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^3.0.0", - "universalify": "^0.1.0" - } - }, - "fs-minipass": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", - "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", - "dev": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "g-status": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/g-status/-/g-status-2.0.2.tgz", - "integrity": "sha512-kQoE9qH+T1AHKgSSD0Hkv98bobE90ILQcXAF4wvGgsr7uFqNvwmh8j+Lq3l0RVt3E3HjSbv2B9biEGcEtpHLCA==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "matcher": "^1.0.0", - "simple-git": "^1.85.0" - } - }, - "gauge": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", - "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", - "dev": true, - "requires": { - "ansi": "^0.3.0", - "has-unicode": "^2.0.0", - "lodash.pad": "^4.1.0", - "lodash.padend": "^4.1.0", - "lodash.padstart": "^4.1.0" - } - }, - "generator-jhipster": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/generator-jhipster/-/generator-jhipster-5.8.2.tgz", - "integrity": "sha512-GW33cKnIf0wWuK411U9GDiNKwKVLbL4BpAqyR4b9JD6ClhwHsQg3HRlItN8pV0iTHJtyFnSfcV/9nJIHFWwvKA==", - "dev": true, - "requires": { - "axios": "0.18.0", - "chalk": "2.4.1", - "commander": "2.16.0", - "conf": "2.0.0", - "didyoumean": "1.2.1", - "ejs": "2.6.1", - "glob": "7.1.2", - "gulp-filter": "5.1.0", - "insight": "0.10.1", - "jhipster-core": "3.6.11", - "js-object-pretty-print": "0.3.0", - "js-yaml": "3.12.0", - "lodash": "4.17.10", - "meow": "5.0.0", - "mkdirp": "0.5.1", - "os-locale": "2.1.0", - "parse-gitignore": "1.0.1", - "pluralize": "7.0.0", - "prettier": "1.13.7", - "randexp": "0.4.9", - "semver": "5.5.0", - "shelljs": "0.8.2", - "tabtab": "2.2.2", - "through2": "2.0.3", - "uuid": "3.3.2", - "yeoman-environment": "2.3.0", - "yeoman-generator": "3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "axios": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", - "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", - "dev": true, - "requires": { - "follow-redirects": "^1.3.0", - "is-buffer": "^1.1.5" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "commander": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", - "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==", - "dev": true - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true - }, - "prettier": { - "version": "1.13.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.13.7.tgz", - "integrity": "sha512-KIU72UmYPGk4MujZGYMFwinB7lOf2LsDNGSOC8ufevsrPLISrZbNJlWstRi3m0AMuszbH+EFSQ/r6w56RSPK6w==", - "dev": true - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - }, - "shelljs": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.2.tgz", - "integrity": "sha512-pRXeNrCA2Wd9itwhvLp5LZQvPJ0wU6bcjaTMywHHGX5XWhVN2nzSu7WV0q+oUY7mGK3mgSkDDzP3MgjqdyIgbQ==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" - } - } - } - }, - "genfun": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", - "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", - "dev": true - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", - "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==", - "dev": true - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "gh-got": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gh-got/-/gh-got-6.0.0.tgz", - "integrity": "sha512-F/mS+fsWQMo1zfgG9MD8KWvTWPPzzhuVwY++fhQ5Ggd+0P+CAMHtzMZhNxG+TqGfHDChJKsbh6otfMGqO2AKBw==", - "dev": true, - "requires": { - "got": "^7.0.0", - "is-plain-obj": "^1.1.0" - } - }, - "github-username": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/github-username/-/github-username-4.1.0.tgz", - "integrity": "sha1-y+KABBiDIG2kISrp5LXxacML9Bc=", - "dev": true, - "requires": { - "gh-got": "^6.0.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", - "dev": true - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", - "dev": true - }, - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "got": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", - "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", - "dev": true, - "requires": { - "decompress-response": "^3.2.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-plain-obj": "^1.1.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "p-cancelable": "^0.3.0", - "p-timeout": "^1.1.1", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "url-parse-lax": "^1.0.0", - "url-to-options": "^1.0.1" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "grouped-queue": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-0.3.3.tgz", - "integrity": "sha1-wWfSpTGcWg4JZO9qJbfC34mWyFw=", - "dev": true, - "requires": { - "lodash": "^4.17.2" - } - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true - }, - "gulp-filter": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/gulp-filter/-/gulp-filter-5.1.0.tgz", - "integrity": "sha1-oF4Rr/sHz33PQafeHLe2OsN4PnM=", - "dev": true, - "requires": { - "multimatch": "^2.0.0", - "plugin-error": "^0.1.2", - "streamfilter": "^1.0.5" - } - }, - "handle-thing": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", - "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", - "dev": true - }, - "handlebars": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.1.tgz", - "integrity": "sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA==", - "dev": true, - "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "dev": true, - "requires": { - "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbol-support-x": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "dev": true, - "requires": { - "has-symbol-support-x": "^1.4.1" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hoek": { - "version": "4.2.1", - "resolved": "http://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", - "dev": true - }, - "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, - "html-comment-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.1" - } - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "html-loader": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-0.5.5.tgz", - "integrity": "sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog==", - "dev": true, - "requires": { - "es6-templates": "^0.2.3", - "fastparse": "^1.1.1", - "html-minifier": "^3.5.8", - "loader-utils": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "html-minifier": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", - "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", - "dev": true, - "requires": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.2.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - } - } - }, - "html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", - "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", - "dev": true, - "requires": { - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "tapable": "^1.0.0", - "toposort": "^1.0.0", - "util.promisify": "1.0.0" - }, - "dependencies": { - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - } - } - }, - "htmlparser2": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", - "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", - "dev": true, - "requires": { - "domelementtype": "1", - "domhandler": "2.1", - "domutils": "1.1", - "readable-stream": "1.0" - }, - "dependencies": { - "domutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", - "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", - "dev": true - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "dependencies": { - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - } - } - }, - "http-parser-js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", - "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", - "dev": true - }, - "http-proxy": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.15.2.tgz", - "integrity": "sha1-ZC/cr/5S00SNK9o7AHnpQJBk2jE=", - "dev": true, - "requires": { - "eventemitter3": "1.x.x", - "requires-port": "1.x.x" - } - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "http-proxy-middleware": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", - "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", - "dev": true, - "requires": { - "http-proxy": "^1.16.2", - "is-glob": "^4.0.0", - "lodash": "^4.17.5", - "micromatch": "^3.1.9" - }, - "dependencies": { - "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", - "dev": true - }, - "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", - "dev": true, - "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", - "dev": true, - "requires": { - "agent-base": "^4.1.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "husky": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/husky/-/husky-1.3.1.tgz", - "integrity": "sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==", - "dev": true, - "requires": { - "cosmiconfig": "^5.0.7", - "execa": "^1.0.0", - "find-up": "^3.0.0", - "get-stdin": "^6.0.0", - "is-ci": "^2.0.0", - "pkg-dir": "^3.0.0", - "please-upgrade-node": "^3.1.1", - "read-pkg": "^4.0.1", - "run-node": "^1.0.0", - "slash": "^2.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "read-pkg": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", - "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=", - "dev": true, - "requires": { - "normalize-package-data": "^2.3.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, - "icss-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.0.0.tgz", - "integrity": "sha512-bA/xGiwWM17qjllIs9X/y0EjsB7e0AV08F3OL8UPsoNkNRibIuu8f1eKTnQ8QO1DteKKTxTUAn+IEWUToIwGOA==", - "dev": true, - "requires": { - "postcss": "^7.0.5" - } - }, - "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "immutable": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", - "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=", - "dev": true - }, - "import-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", - "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", - "dev": true, - "requires": { - "import-from": "^2.1.0" - } - }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - } - }, - "import-from": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", - "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inquirer": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.0.6.tgz", - "integrity": "sha1-4EqqnQW3o8ubD0B9BDdfBEcZA0c=", - "requires": { - "ansi-escapes": "^1.1.0", - "chalk": "^1.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.1", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx": "^4.1.0", - "string-width": "^2.0.0", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" - } - }, - "insight": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/insight/-/insight-0.10.1.tgz", - "integrity": "sha512-kLGeYQkh18f8KuC68QKdi0iwUcIaayJVB/STpX7x452/7pAUm1yfG4giJwcxbrTh0zNYtc8kBR+6maLMOzglOQ==", - "dev": true, - "requires": { - "async": "^2.1.4", - "chalk": "^2.3.0", - "conf": "^1.3.1", - "inquirer": "^5.0.0", - "lodash.debounce": "^4.0.8", - "os-name": "^2.0.1", - "request": "^2.74.0", - "tough-cookie": "^2.0.0", - "uuid": "^3.0.0" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "conf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/conf/-/conf-1.4.0.tgz", - "integrity": "sha512-bzlVWS2THbMetHqXKB8ypsXN4DQ/1qopGwNJi1eYbpwesJcd86FBjFciCQX/YwAhp9bM7NVnPFqZ5LpV7gP0Dg==", - "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "env-paths": "^1.0.0", - "make-dir": "^1.0.0", - "pkg-up": "^2.0.0", - "write-file-atomic": "^2.3.0" - } - }, - "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } - }, - "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", - "dev": true, - "requires": { - "symbol-observable": "1.0.1" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - } - } - }, - "internal-ip": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz", - "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==", - "dev": true, - "requires": { - "default-gateway": "^2.6.0", - "ipaddr.js": "^1.5.2" - } - }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", - "dev": true - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dev": true, - "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "^2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-generator-fn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.0.0.tgz", - "integrity": "sha512-elzyIdM7iKoFHzcrndIqjYomImhxrFRnGP3galODoII4TB9gI7mZ+FnlLQmmjf27SxHS2gKEeyhX5/+YRS6H9g==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-number-like": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", - "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", - "dev": true, - "requires": { - "lodash.isfinite": "^3.3.2" - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true - }, - "is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "dev": true, - "requires": { - "symbol-observable": "^1.1.0" - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", - "dev": true - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true - }, - "is-scoped": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-1.0.0.tgz", - "integrity": "sha1-RJypgpnnEwOCViieyytUDcQ3yzA=", - "dev": true, - "requires": { - "scoped-regex": "^1.0.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, - "requires": { - "buffer-alloc": "^1.2.0" - } - }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "dev": true, - "requires": { - "punycode": "2.x.x" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz", - "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==", - "dev": true, - "requires": { - "async": "^2.6.1", - "compare-versions": "^3.2.1", - "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.3", - "istanbul-lib-hook": "^2.0.3", - "istanbul-lib-instrument": "^3.1.0", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.2", - "istanbul-reports": "^2.1.1", - "js-yaml": "^3.12.0", - "make-dir": "^1.3.0", - "minimatch": "^3.0.4", - "once": "^1.4.0" - }, - "dependencies": { - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - } - } - }, - "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", - "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", - "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", - "dev": true, - "requires": { - "@babel/generator": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "istanbul-lib-coverage": "^2.0.3", - "semver": "^5.5.0" - } - }, - "istanbul-lib-report": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", - "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "supports-color": "^6.0.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", - "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "rimraf": "^2.6.2", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz", - "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", - "dev": true, - "requires": { - "handlebars": "^4.1.0" - } - }, - "istextorbinary": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.5.1.tgz", - "integrity": "sha512-pv/JNPWnfpwGjPx7JrtWTwsWsxkrK3fNzcEVnt92YKEIErps4Fsk49+qzCe9iQF2hjqK8Naqf8P9kzoeCuQI1g==", - "dev": true, - "requires": { - "binaryextensions": "^2.1.2", - "editions": "^2.1.3", - "textextensions": "^2.4.0" - } - }, - "isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "dev": true, - "requires": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" - } - }, - "jest": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.1.0.tgz", - "integrity": "sha512-+q91L65kypqklvlRFfXfdzUKyngQLOcwGhXQaLmVHv+d09LkNXuBuGxlofTFW42XMzu3giIcChchTsCNUjQ78A==", - "dev": true, - "requires": { - "import-local": "^2.0.0", - "jest-cli": "^24.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "camelcase": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.0.tgz", - "integrity": "sha512-Y05ICatFYPAfykDIB7VdwSJ0LUl1yq/BwO2OpyGGLjiRe1fgzTwVypPiWnzkGFOVFHXrCXUNBl86bpjBhZWSJg==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "jest-cli": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.5.0.tgz", - "integrity": "sha512-P+Jp0SLO4KWN0cGlNtC7JV0dW1eSFR7eRpoOucP2UM0sqlzp/bVHeo71Omonvigrj9AvCKy7NtQANtqJ7FXz8g==", - "dev": true, - "requires": { - "@jest/core": "^24.5.0", - "@jest/test-result": "^24.5.0", - "@jest/types": "^24.5.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "import-local": "^2.0.0", - "is-ci": "^2.0.0", - "jest-config": "^24.5.0", - "jest-util": "^24.5.0", - "jest-validate": "^24.5.0", - "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^12.0.2" - } - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "jest-changed-files": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.5.0.tgz", - "integrity": "sha512-Ikl29dosYnTsH9pYa1Tv9POkILBhN/TLZ37xbzgNsZ1D2+2n+8oEZS2yP1BrHn/T4Rs4Ggwwbp/x8CKOS5YJOg==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - } - } - }, - "jest-config": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.5.0.tgz", - "integrity": "sha512-t2UTh0Z2uZhGBNVseF8wA2DS2SuBiLOL6qpLq18+OZGfFUxTM7BzUVKyHFN/vuN+s/aslY1COW95j1Rw81huOQ==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.5.0", - "babel-jest": "^24.5.0", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.5.0", - "jest-environment-node": "^24.5.0", - "jest-get-type": "^24.3.0", - "jest-jasmine2": "^24.5.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.5.0", - "jest-util": "^24.5.0", - "jest-validate": "^24.5.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.5.0", - "realpath-native": "^1.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-diff": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.5.0.tgz", - "integrity": "sha512-mCILZd9r7zqL9Uh6yNoXjwGQx0/J43OD2vvWVKwOEOLZliQOsojXwqboubAQ+Tszrb6DHGmNU7m4whGeB9YOqw==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.5.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-docblock": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", - "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", - "dev": true, - "requires": { - "detect-newline": "^2.1.0" - } - }, - "jest-each": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.5.0.tgz", - "integrity": "sha512-6gy3Kh37PwIT5sNvNY2VchtIFOOBh8UCYnBlxXMb5sr5wpJUDPTUATX2Axq1Vfk+HWTMpsYPeVYp4TXx5uqUBw==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "jest-util": "^24.5.0", - "pretty-format": "^24.5.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-environment-jsdom": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.5.0.tgz", - "integrity": "sha512-62Ih5HbdAWcsqBx2ktUnor/mABBo1U111AvZWcLKeWN/n/gc5ZvDBKe4Og44fQdHKiXClrNGC6G0mBo6wrPeGQ==", - "dev": true, - "requires": { - "@jest/environment": "^24.5.0", - "@jest/fake-timers": "^24.5.0", - "@jest/types": "^24.5.0", - "jest-mock": "^24.5.0", - "jest-util": "^24.5.0", - "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.5.0.tgz", - "integrity": "sha512-du6FuyWr/GbKLsmAbzNF9mpr2Iu2zWSaq/BNHzX+vgOcts9f2ayXBweS7RAhr+6bLp6qRpMB6utAMF5Ygktxnw==", - "dev": true, - "requires": { - "@jest/environment": "^24.5.0", - "@jest/fake-timers": "^24.5.0", - "@jest/types": "^24.5.0", - "jest-mock": "^24.5.0", - "jest-util": "^24.5.0" - } - }, - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - }, - "jest-haste-map": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.5.0.tgz", - "integrity": "sha512-mb4Yrcjw9vBgSvobDwH8QUovxApdimGcOkp+V1ucGGw4Uvr3VzZQBJhNm1UY3dXYm4XXyTW2G7IBEZ9pM2ggRQ==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.5.0", - "jest-worker": "^24.4.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3" - } - }, - "jest-jasmine2": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.5.0.tgz", - "integrity": "sha512-sfVrxVcx1rNUbBeyIyhkqZ4q+seNKyAG6iM0S2TYBdQsXjoFDdqWFfsUxb6uXSsbimbXX/NMkJIwUZ1uT9+/Aw==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.5.0", - "@jest/test-result": "^24.5.0", - "@jest/types": "^24.5.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.5.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.5.0", - "jest-matcher-utils": "^24.5.0", - "jest-message-util": "^24.5.0", - "jest-runtime": "^24.5.0", - "jest-snapshot": "^24.5.0", - "jest-util": "^24.5.0", - "pretty-format": "^24.5.0", - "throat": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-junit": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-6.2.1.tgz", - "integrity": "sha512-zMbwKzZGo9TQOjdBUNQdTEf81QvOrwiQtLIUSEyCwPXYx/G/DJGXEJcqs8viXxn6poJ4Xh4pYGDLD0DKDwtfVA==", - "dev": true, - "requires": { - "jest-validate": "^24.0.0", - "mkdirp": "^0.5.1", - "strip-ansi": "^4.0.0", - "xml": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "jest-leak-detector": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.5.0.tgz", - "integrity": "sha512-LZKBjGovFRx3cRBkqmIg+BZnxbrLqhQl09IziMk3oeh1OV81Hg30RUIx885mq8qBv1PA0comB9bjKcuyNO1bCQ==", - "dev": true, - "requires": { - "pretty-format": "^24.5.0" - } - }, - "jest-matcher-utils": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.5.0.tgz", - "integrity": "sha512-QM1nmLROjLj8GMGzg5VBra3I9hLpjMPtF1YqzQS3rvWn2ltGZLrGAO1KQ9zUCVi5aCvrkbS5Ndm2evIP9yZg1Q==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.5.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.5.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-message-util": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.5.0.tgz", - "integrity": "sha512-6ZYgdOojowCGiV0D8WdgctZEAe+EcFU+KrVds+0ZjvpZurUW2/oKJGltJ6FWY2joZwYXN5VL36GPV6pNVRqRnQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.5.0", - "@jest/types": "^24.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-mock": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.5.0.tgz", - "integrity": "sha512-ZnAtkWrKf48eERgAOiUxVoFavVBziO2pAi2MfZ1+bGXVkDfxWLxU0//oJBkgwbsv6OAmuLBz4XFFqvCFMqnGUw==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", - "dev": true - }, - "jest-preset-angular": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-6.0.2.tgz", - "integrity": "sha512-uhrllY41tUvkeR41aX9bU5w3/EvvmwZiJ3UitDhRSEJL2Jvq2N/xKlmw7qvlZoGZnciFjOUJ2WDKv5fmCrvnQA==", - "dev": true, - "requires": { - "@types/jest": "^23.3.1", - "jest-zone-patch": ">=0.0.9 <1.0.0", - "ts-jest": "~23.1.3" - }, - "dependencies": { - "@types/jest": { - "version": "23.3.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.3.14.tgz", - "integrity": "sha512-Q5hTcfdudEL2yOmluA1zaSyPbzWPmJ3XfSWeP3RyoYvS9hnje1ZyagrZOuQ6+1nQC1Gw+7gap3pLNL3xL6UBug==", - "dev": true - } - } - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-resolve": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.5.0.tgz", - "integrity": "sha512-ZIfGqLX1Rg8xJpQqNjdoO8MuxHV1q/i2OO1hLXjgCWFWs5bsedS8UrOdgjUqqNae6DXA+pCyRmdcB7lQEEbXew==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-resolve-dependencies": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.5.0.tgz", - "integrity": "sha512-dRVM1D+gWrFfrq2vlL5P9P/i8kB4BOYqYf3S7xczZ+A6PC3SgXYSErX/ScW/469pWMboM1uAhgLF+39nXlirCQ==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.5.0" - } - }, - "jest-runner": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.5.0.tgz", - "integrity": "sha512-oqsiS9TkIZV5dVkD+GmbNfWBRPIvxqmlTQ+AQUJUQ07n+4xTSDc40r+aKBynHw9/tLzafC00DIbJjB2cOZdvMA==", - "dev": true, - "requires": { - "@jest/console": "^24.3.0", - "@jest/environment": "^24.5.0", - "@jest/test-result": "^24.5.0", - "@jest/types": "^24.5.0", - "chalk": "^2.4.2", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.5.0", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.5.0", - "jest-jasmine2": "^24.5.0", - "jest-leak-detector": "^24.5.0", - "jest-message-util": "^24.5.0", - "jest-resolve": "^24.5.0", - "jest-runtime": "^24.5.0", - "jest-util": "^24.5.0", - "jest-worker": "^24.4.0", - "source-map-support": "^0.5.6", - "throat": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-runtime": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.5.0.tgz", - "integrity": "sha512-GTFHzfLdwpaeoDPilNpBrorlPoNZuZrwKKzKJs09vWwHo+9TOsIIuszK8cWOuKC7ss07aN1922Ge8fsGdsqCuw==", - "dev": true, - "requires": { - "@jest/console": "^24.3.0", - "@jest/environment": "^24.5.0", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.5.0", - "@jest/types": "^24.5.0", - "@types/yargs": "^12.0.2", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.5.0", - "jest-haste-map": "^24.5.0", - "jest-message-util": "^24.5.0", - "jest-mock": "^24.5.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.5.0", - "jest-snapshot": "^24.5.0", - "jest-util": "^24.5.0", - "jest-validate": "^24.5.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^12.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "camelcase": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.0.tgz", - "integrity": "sha512-Y05ICatFYPAfykDIB7VdwSJ0LUl1yq/BwO2OpyGGLjiRe1fgzTwVypPiWnzkGFOVFHXrCXUNBl86bpjBhZWSJg==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", - "dev": true - }, - "jest-snapshot": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.5.0.tgz", - "integrity": "sha512-eBEeJb5ROk0NcpodmSKnCVgMOo+Qsu5z9EDl3tGffwPzK1yV37mjGWF2YeIz1NkntgTzP+fUL4s09a0+0dpVWA==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.5.0", - "chalk": "^2.0.1", - "expect": "^24.5.0", - "jest-diff": "^24.5.0", - "jest-matcher-utils": "^24.5.0", - "jest-message-util": "^24.5.0", - "jest-resolve": "^24.5.0", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.5.0", - "semver": "^5.5.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-sonar-reporter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jest-sonar-reporter/-/jest-sonar-reporter-2.0.0.tgz", - "integrity": "sha512-ZervDCgEX5gdUbdtWsjdipLN3bKJwpxbvhkYNXTAYvAckCihobSLr9OT/IuyNIRT1EZMDDwR6DroWtrq+IL64w==", - "dev": true, - "requires": { - "xml": "^1.0.1" - } - }, - "jest-util": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.5.0.tgz", - "integrity": "sha512-Xy8JsD0jvBz85K7VsTIQDuY44s+hYJyppAhcsHsOsGisVtdhar6fajf2UOf2mEVEgh15ZSdA0zkCuheN8cbr1Q==", - "dev": true, - "requires": { - "@jest/console": "^24.3.0", - "@jest/fake-timers": "^24.5.0", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.5.0", - "@jest/types": "^24.5.0", - "@types/node": "*", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "callsites": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", - "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-validate": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.5.0.tgz", - "integrity": "sha512-gg0dYszxjgK2o11unSIJhkOFZqNRQbWOAB2/LOUdsd2LfD9oXiMeuee8XsT0iRy5EvSccBgB4h/9HRbIo3MHgQ==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0", - "camelcase": "^5.0.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "leven": "^2.1.0", - "pretty-format": "^24.5.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "camelcase": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.0.tgz", - "integrity": "sha512-Y05ICatFYPAfykDIB7VdwSJ0LUl1yq/BwO2OpyGGLjiRe1fgzTwVypPiWnzkGFOVFHXrCXUNBl86bpjBhZWSJg==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-watcher": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.5.0.tgz", - "integrity": "sha512-/hCpgR6bg0nKvD3nv4KasdTxuhwfViVMHUATJlnGCD0r1QrmIssimPbmc5KfAQblAVxkD8xrzuij9vfPUk1/rA==", - "dev": true, - "requires": { - "@jest/test-result": "^24.5.0", - "@jest/types": "^24.5.0", - "@types/node": "*", - "@types/yargs": "^12.0.9", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.5.0", - "string-length": "^2.0.0" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-worker": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.4.0.tgz", - "integrity": "sha512-BH9X/klG9vxwoO99ZBUbZFfV8qO0XNZ5SIiCyYK2zOuJBl6YJVAeNIQjcoOVNu4HGEHeYEKsUWws8kSlSbZ9YQ==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^1.0.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-zone-patch": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/jest-zone-patch/-/jest-zone-patch-0.0.10.tgz", - "integrity": "sha512-K5uHLHgMgi2Eyj74gbY+xSeGGekb5U48bXsgDwgipRbFdaekyZK+TAcp8auamqU4UjrAt5S4sIUZz/2bBNyTTA==", - "dev": true - }, - "jhipster-core": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jhipster-core/-/jhipster-core-3.6.11.tgz", - "integrity": "sha512-OibJay1+nwKk+mfRfuTrt2rW2h7BmGNfKWR7TQO4oYqG+I096EvJxZkIMCcjA9KuKLOZvnzfEi4UbtKyRTmHkg==", - "dev": true, - "requires": { - "chevrotain": "4.2.0", - "fs-extra": "7.0.1", - "lodash": "4.17.11", - "winston": "3.2.1" - }, - "dependencies": { - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - } - } - }, - "joi": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-11.4.0.tgz", - "integrity": "sha512-O7Uw+w/zEWgbL6OcHbyACKSj0PkQeUgmehdoXVSxt92QFCq4+1390Rwh5moI2K/OgC7D8RHRZqHZxT2husMJHA==", - "dev": true, - "requires": { - "hoek": "4.x.x", - "isemail": "3.x.x", - "topo": "2.x.x" - } - }, - "js-object-pretty-print": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/js-object-pretty-print/-/js-object-pretty-print-0.3.0.tgz", - "integrity": "sha1-RnDkUAZu4ezPNRdMfRl/WqOLz3Q=", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonfile": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", - "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jstransform": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-11.0.3.tgz", - "integrity": "sha1-CaeJk+CuTU70SH9hVakfYZDLQiM=", - "dev": true, - "requires": { - "base62": "^1.1.0", - "commoner": "^0.10.1", - "esprima-fb": "^15001.1.0-dev-harmony-fb", - "object-assign": "^2.0.0", - "source-map": "^0.4.2" - }, - "dependencies": { - "esprima-fb": { - "version": "15001.1.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz", - "integrity": "sha1-MKlHMDxrjV6VW+4rmbHSMyBqaQE=", - "dev": true - }, - "object-assign": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", - "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "kleur": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.2.tgz", - "integrity": "sha512-3h7B2WRT5LNXOtQiAaWonilegHcPSf9nLVXlSTci8lu1dZUuui61+EsPEZqSVxY7rXYmB2DVKMQILxaO5WL61Q==", - "dev": true - }, - "kuler": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", - "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", - "dev": true, - "requires": { - "colornames": "^1.1.1" - } - }, - "last-call-webpack-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", - "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", - "dev": true, - "requires": { - "lodash": "^4.17.5", - "webpack-sources": "^1.1.0" - } - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "^1.0.0" - } - }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, - "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "limiter": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.4.tgz", - "integrity": "sha512-XCpr5bElgDI65vVgstP8TWjv6/QKWm9GU5UG0Pr5sLQ3QLo8NVKsioe+Jed5/3vFOe3IQuqE7DKwTvKQkjTHvg==", - "dev": true - }, - "lint-staged": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.3.tgz", - "integrity": "sha512-6TGkikL1B+6mIOuSNq2TV6oP21IhPMnV8q0cf9oYZ296ArTVNcbFh1l1pfVOHHbBIYLlziWNsQ2q45/ffmJ4AA==", - "dev": true, - "requires": { - "@iamstarkov/listr-update-renderer": "0.4.1", - "chalk": "^2.3.1", - "commander": "^2.14.1", - "cosmiconfig": "^5.0.2", - "debug": "^3.1.0", - "dedent": "^0.7.0", - "del": "^3.0.0", - "execa": "^1.0.0", - "find-parent-dir": "^0.3.0", - "g-status": "^2.0.2", - "is-glob": "^4.0.0", - "is-windows": "^1.0.2", - "listr": "^0.14.2", - "lodash": "^4.17.5", - "log-symbols": "^2.2.0", - "micromatch": "^3.1.8", - "npm-which": "^3.0.1", - "p-map": "^1.1.1", - "path-is-inside": "^1.0.2", - "pify": "^3.0.0", - "please-upgrade-node": "^3.0.2", - "staged-git-files": "1.1.2", - "string-argv": "^0.0.2", - "stringify-object": "^3.2.2", - "yup": "^0.26.10" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "listr": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", - "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", - "dev": true, - "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", - "is-observable": "^1.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.5.0", - "listr-verbose-renderer": "^0.5.0", - "p-map": "^2.0.0", - "rxjs": "^6.3.3" - }, - "dependencies": { - "p-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.0.0.tgz", - "integrity": "sha512-GO107XdrSUmtHxVoi60qc9tUl/KkNKm+X2CF4P9amalpGxv5YqVPJNfSb0wcA+syCopkZvYYIzW8OVTQW59x/w==", - "dev": true - } - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", - "dev": true - }, - "listr-update-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", - "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - } - } - } - }, - "listr-verbose-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", - "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "cli-cursor": "^2.1.0", - "date-fns": "^1.27.2", - "figures": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "localtunnel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-1.9.1.tgz", - "integrity": "sha512-HWrhOslklDvxgOGFLxi6fQVnvpl6XdX4sPscfqMZkzi3gtt9V7LKBWYvNUcpHSVvjwCQ6xzXacVvICNbNcyPnQ==", - "dev": true, - "requires": { - "axios": "0.17.1", - "debug": "2.6.9", - "openurl": "1.1.1", - "yargs": "6.6.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^1.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", - "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^4.2.0" - } - }, - "yargs-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", - "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", - "dev": true, - "requires": { - "camelcase": "^3.0.0" - } - } - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, - "lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", - "dev": true - }, - "lodash.isfinite": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", - "integrity": "sha1-+4m2WpqAKBgz8LdHizpRBPiY67M=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.pad": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", - "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=", - "dev": true - }, - "lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", - "dev": true - }, - "lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "lodash.template": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", - "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", - "dev": true, - "requires": { - "lodash._reinterpolate": "~3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", - "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", - "dev": true, - "requires": { - "lodash._reinterpolate": "~3.0.0" - } - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - } - } - } - }, - "logform": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", - "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", - "dev": true, - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^2.3.3", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "loglevel": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", - "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "macos-release": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-1.1.0.tgz", - "integrity": "sha512-mmLbumEYMi5nXReB9js3WGsB8UE6cDBWyIO62Z4DNx6GbRhDxHNjA1MlzSpJ2S2KM1wyiPRA0d19uHWYYvMHjA==", - "dev": true - }, - "magic-string": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.1.tgz", - "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.1" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "make-fetch-happen": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz", - "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==", - "dev": true, - "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^11.0.1", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "matcher": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-1.1.1.tgz", - "integrity": "sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.4" - } - }, - "math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", - "dev": true - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "mdn-data": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", - "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "mem-fs": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-1.1.3.tgz", - "integrity": "sha1-uK6NLj/Lb10/kWXBLUVRoGXZicw=", - "dev": true, - "requires": { - "through2": "^2.0.0", - "vinyl": "^1.1.0", - "vinyl-file": "^2.0.0" - } - }, - "mem-fs-editor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-5.1.0.tgz", - "integrity": "sha512-2Yt2GCYEbcotYbIJagmow4gEtHDqzpq5XN94+yAx/NT5+bGqIjkXnm3KCUQfE6kRfScGp9IZknScoGRKu8L78w==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "deep-extend": "^0.6.0", - "ejs": "^2.5.9", - "glob": "^7.0.3", - "globby": "^8.0.1", - "isbinaryfile": "^3.0.2", - "mkdirp": "^0.5.0", - "multimatch": "^2.0.0", - "rimraf": "^2.2.8", - "through2": "^2.0.0", - "vinyl": "^2.0.1" - }, - "dependencies": { - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "path-type": "^3.0.0" - } - }, - "globby": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", - "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "dir-glob": "2.0.0", - "fast-glob": "^2.0.2", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - }, - "vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", - "dev": true, - "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - } - } - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "meow": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", - "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", - "dev": true, - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0", - "yargs-parser": "^10.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merge-jsons-webpack-plugin": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/merge-jsons-webpack-plugin/-/merge-jsons-webpack-plugin-1.0.18.tgz", - "integrity": "sha512-wUcK5k9XzsquOcV6TtO7ZA/kWx+LVGuOk/+YjzWgVINWh8CAjANShtgTwEE87ewN3bcZ4PMyWQbEGDpIQh30xg==", - "dev": true, - "requires": { - "es6-promise": "4.0.5", - "glob": "7.1.1" - }, - "dependencies": { - "es6-promise": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", - "integrity": "sha1-eILzCt3lskDM+n99eMVIMwlRrkI=", - "dev": true - }, - "glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "merge2": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", - "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - }, - "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", - "dev": true - }, - "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", - "dev": true, - "requires": { - "mime-db": "~1.38.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", - "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" - } - }, - "minipass": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", - "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", - "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", - "dev": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mitt": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.1.3.tgz", - "integrity": "sha512-mUDCnVNsAi+eD6qA0HkRkwYczbLHJ49z17BGe2PYRhZL4wpZUFZGJHU7/5tmvohoma+Hdn0Vh/oJTiPEmgSruA==", - "dev": true - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, - "moment-locales-webpack-plugin": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/moment-locales-webpack-plugin/-/moment-locales-webpack-plugin-1.0.7.tgz", - "integrity": "sha512-KjYpaAhmuzGFZl6534FlZoK7QtW3vqlxd+A17W9DlgHJ5yhXANy7AZJl7iYtZpWjAfMTAWiVrQ7YDZdkFO6uRw==", - "dev": true, - "requires": { - "lodash.difference": "^4.5.0" - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "multimatch": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", - "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", - "dev": true, - "requires": { - "array-differ": "^1.0.0", - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "minimatch": "^3.0.0" - } - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" - }, - "nan": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "neo-async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", - "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", - "dev": true - }, - "ng-jhipster": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/ng-jhipster/-/ng-jhipster-0.9.1.tgz", - "integrity": "sha512-/ViJ6bNtc/4w6valKNvHKyvXF1cn1OBnSEh3Q8aEUHtmoyr9owReTGHT7ylNclthUsxztRMUS4GrMMEQ5kM09Q==", - "requires": { - "tslib": "^1.9.0" - } - }, - "ngx-cookie": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ngx-cookie/-/ngx-cookie-2.0.1.tgz", - "integrity": "sha512-3+agXZkoPxRP3IyELf7Eiuhk6TX+EAX974kkCR6Xjm+N7boEA+Fin2Q90AAE4XZzY48skkVzLH96TOikb5yU3g==" - }, - "ngx-infinite-scroll": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-7.0.1.tgz", - "integrity": "sha512-be9DAAuabV7VGI06/JUnS6pXC6mcBOzA4+SBCwOcP9WwJ2r5GjdZyOa34ls9hi1MnCOj3zrXLvPKQ/UDp6csIw==", - "requires": { - "opencollective": "^1.0.3" - } - }, - "ngx-webstorage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ngx-webstorage/-/ngx-webstorage-2.0.1.tgz", - "integrity": "sha512-AhBkl1v5sBLYiGC1DuHxM90B8OewqyhYhm+KGtJIFxMh5dj3tlNgPokmWCtKcUZF26m8MgxDDuP5e6NeDCpYQw==" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "^1.1.1" - } - }, - "node-fetch": { - "version": "1.6.3", - "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", - "integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "node-fetch-npm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", - "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", - "dev": true, - "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" - } - }, - "node-forge": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-libs-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", - "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-notifier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", - "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", - "dev": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" - } - }, - "node-releases": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.12.tgz", - "integrity": "sha512-Y+AQ1xdjcgaEzpL65PBEF3fnl1FNKnDh9Zm+AUQLIlyyqtSc4u93jyMN4zrjMzdwKQ10RTr3tgY1x7qpsfF/xg==", - "dev": true, - "requires": { - "semver": "^5.3.0" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", - "dev": true - }, - "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", - "dev": true - }, - "npm-package-arg": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", - "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.6.0", - "osenv": "^0.1.5", - "semver": "^5.5.0", - "validate-npm-package-name": "^3.0.0" - } - }, - "npm-packlist": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", - "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", - "dev": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npm-path": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz", - "integrity": "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==", - "dev": true, - "requires": { - "which": "^1.2.10" - } - }, - "npm-pick-manifest": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", - "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" - } - }, - "npm-registry-fetch": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.8.0.tgz", - "integrity": "sha512-hrw8UMD+Nob3Kl3h8Z/YjmKamb1gf7D1ZZch2otrIXM3uFLB5vjEY6DhMlq80z/zZet6eETLbOXcuQudCB3Zpw==", - "dev": true, - "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^4.1.3", - "make-fetch-happen": "^4.0.1", - "npm-package-arg": "^6.1.0" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "npm-which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz", - "integrity": "sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=", - "dev": true, - "requires": { - "commander": "^2.9.0", - "npm-path": "^2.0.2", - "which": "^1.2.10" - } - }, - "npmlog": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.4.tgz", - "integrity": "sha1-mLUlMPJRTKkNCexbIsiEZyI3VpI=", - "dev": true, - "requires": { - "ansi": "~0.3.1", - "are-we-there-yet": "~1.1.2", - "gauge": "~1.2.5" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "nwsapi": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.3.tgz", - "integrity": "sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", - "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", - "dev": true - }, - "object-path": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz", - "integrity": "sha1-D9mnT8X60a45aLWGvaXGMr1sBaU=", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", - "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", - "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=", - "dev": true - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "opencollective": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/opencollective/-/opencollective-1.0.3.tgz", - "integrity": "sha1-ruY3K8KBRFg2kMPKja7PwSDdDvE=", - "requires": { - "babel-polyfill": "6.23.0", - "chalk": "1.1.3", - "inquirer": "3.0.6", - "minimist": "1.2.0", - "node-fetch": "1.6.3", - "opn": "4.0.2" - } - }, - "openurl": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz", - "integrity": "sha1-OHW0sO96UsFW8NtB1GCduw+Us4c=", - "dev": true - }, - "opn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", - "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", - "requires": { - "object-assign": "^4.0.1", - "pinkie-promise": "^2.0.0" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - } - } - }, - "optimize-css-assets-webpack-plugin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.1.tgz", - "integrity": "sha512-Rqm6sSjWtx9FchdP0uzTQDc7GXDKnwVEGoSxjezPkzMewx7gEWE9IMUYKmigTRC4U3RaNSwYVnUDLuIdtTpm0A==", - "dev": true, - "requires": { - "cssnano": "^4.1.0", - "last-call-webpack-plugin": "^3.0.0" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } - } - }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "os-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-2.0.1.tgz", - "integrity": "sha1-uaOGNhwXrjohc27wWZQFyajF3F4=", - "dev": true, - "requires": { - "macos-release": "^1.0.0", - "win-release": "^1.0.0" - } - }, - "os-shim": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", - "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-cancelable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", - "dev": true - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", - "dev": true, - "requires": { - "p-reduce": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", - "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, - "p-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", - "dev": true - }, - "p-timeout": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", - "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", - "dev": true, - "requires": { - "p-finally": "^1.0.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pacote": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.4.0.tgz", - "integrity": "sha512-WQ1KL/phGMkedYEQx9ODsjj7xvwLSpdFJJdEXrLyw5SILMxcTNt5DTxT2Z93fXuLFYJBlZJdnwdalrQdB/rX5w==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "cacache": "^11.3.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^4.0.1", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^2.2.3", - "npm-registry-fetch": "^3.8.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.8", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - } - }, - "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true, - "requires": { - "no-case": "^2.2.0" - } - }, - "parse-asn1": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", - "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-gitignore": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-gitignore/-/parse-gitignore-1.0.1.tgz", - "integrity": "sha512-UGyowyjtx26n65kdAMWhm6/3uy5uSrpcuH7tt+QEVudiBoVS+eqHxD5kbi9oWVRwj7sCzXqwuM+rUGw7earl6A==", - "dev": true - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - } - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "please-upgrade-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz", - "integrity": "sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" - } - }, - "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", - "dev": true, - "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" - }, - "dependencies": { - "arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" - } - }, - "arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", - "dev": true - }, - "extend-shallow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", - "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", - "dev": true, - "requires": { - "kind-of": "^1.1.0" - } - }, - "kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", - "dev": true - } - } - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "portfinder": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", - "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", - "dev": true, - "requires": { - "async": "^1.5.2", - "debug": "^2.2.0", - "mkdirp": "0.5.x" - } - }, - "portscanner": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz", - "integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=", - "dev": true, - "requires": { - "async": "1.5.2", - "is-number-like": "^1.0.3" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", - "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-calc": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz", - "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", - "dev": true, - "requires": { - "css-unit-converter": "^1.1.1", - "postcss": "^7.0.5", - "postcss-selector-parser": "^5.0.0-rc.4", - "postcss-value-parser": "^3.3.1" - }, - "dependencies": { - "cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", - "dev": true - }, - "postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "dev": true, - "requires": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-colormin": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-convert-values": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-load-config": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", - "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", - "dev": true, - "requires": { - "cosmiconfig": "^4.0.0", - "import-cwd": "^2.0.0" - }, - "dependencies": { - "cosmiconfig": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", - "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0", - "require-from-string": "^2.0.1" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - } - } - }, - "postcss-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", - "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "postcss": "^7.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^1.0.0" - } - }, - "postcss-merge-longhand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", - "dev": true, - "requires": { - "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" - } - }, - "postcss-merge-rules": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "cssnano-util-same-parent": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0", - "vendors": "^1.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-minify-font-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-minify-gradients": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-minify-params": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "browserslist": "^4.0.0", - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "uniqs": "^2.0.0" - } - }, - "postcss-minify-selectors": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", - "dev": true, - "requires": { - "postcss": "^7.0.5" - } - }, - "postcss-modules-local-by-default": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", - "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", - "dev": true, - "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0", - "postcss-value-parser": "^3.3.1" - } - }, - "postcss-modules-scope": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", - "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==", - "dev": true, - "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" - } - }, - "postcss-modules-values": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", - "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", - "dev": true, - "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^7.0.6" - } - }, - "postcss-normalize-charset": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-normalize-display-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-positions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-repeat-style": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-string": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", - "dev": true, - "requires": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-timing-functions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-unicode": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", - "dev": true, - "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-whitespace": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-ordered-values": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-reduce-initial": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-selector-parser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", - "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "dependencies": { - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - } - } - }, - "postcss-svgo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", - "dev": true, - "requires": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" - } - }, - "postcss-unique-selectors": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "prettier": { - "version": "1.16.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", - "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==", - "dev": true - }, - "pretty-bytes": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.1.0.tgz", - "integrity": "sha512-wa5+qGVg9Yt7PB6rYm3kXlKzgzgivYTLRandezh43jjRqgyDyP+9YxfJpJiLs9yKD1WeU8/OvtToWpW7255FtA==", - "dev": true - }, - "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "dev": true, - "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" - } - }, - "pretty-format": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.5.0.tgz", - "integrity": "sha512-/3RuSghukCf8Riu5Ncve0iI+BzVkbRU5EeUoArKARZobREycuH5O4waxvaNIloEXdb0qwgmEAed5vTpX1HNROQ==", - "dev": true, - "requires": { - "@jest/types": "^24.5.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - } - } - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "requires": { - "asap": "~2.0.3" - } - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "promise-retry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", - "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", - "dev": true, - "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - } - }, - "prompts": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.4.tgz", - "integrity": "sha512-HTzM3UWp/99A0gk51gAegwo1QRYA7xjcZufMNe33rCclFszUYAuHe1fIN/3ZmiHeGPkUsNaRyQm1hHOfM0PKxA==", - "dev": true, - "requires": { - "kleur": "^3.0.2", - "sisteransi": "^1.0.0" - } - }, - "property-expr": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-1.5.1.tgz", - "integrity": "sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g==", - "dev": true - }, - "protoduck": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", - "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", - "dev": true, - "requires": { - "genfun": "^5.0.0" - } - }, - "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qs": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz", - "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", - "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==", - "dev": true - }, - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", - "dev": true - }, - "randexp": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.9.tgz", - "integrity": "sha512-maAX1cnBkzIZ89O4tSQUOF098xjGMC8N+9vuY/WfHwg87THw6odD2Br35donlj5e6KnB1SB0QBHhTQhhDHuTPQ==", - "dev": true, - "requires": { - "drange": "^1.0.0", - "ret": "^0.2.0" - }, - "dependencies": { - "ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", - "dev": true - } - } - }, - "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "dev": true, - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "react": { - "version": "0.14.9", - "resolved": "https://registry.npmjs.org/react/-/react-0.14.9.tgz", - "integrity": "sha1-kRCmSXxJ1EuhwO3TF67CnC4NkdE=", - "dev": true, - "requires": { - "envify": "^3.0.0", - "fbjs": "^0.6.1" - } - }, - "react-dom": { - "version": "0.14.9", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-0.14.9.tgz", - "integrity": "sha1-BQZKPc8PsYgKOyv8nVjFXY2fYpM=", - "dev": true - }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - }, - "read-chunk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-2.1.0.tgz", - "integrity": "sha1-agTAkoAF7Z1C4aasVgDhnLx/9lU=", - "dev": true, - "requires": { - "pify": "^3.0.0", - "safe-buffer": "^5.1.1" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", - "dev": true, - "requires": { - "util.promisify": "^1.0.0" - } - }, - "recast": { - "version": "0.11.23", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", - "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", - "dev": true, - "requires": { - "ast-types": "0.9.6", - "esprima": "~3.1.0", - "private": "~0.1.5", - "source-map": "~0.5.0" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", - "dev": true, - "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - } - }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexp-to-ast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.3.5.tgz", - "integrity": "sha512-1CJygtdvsfNFwiyjaMLBWtg2tfEqx/jSZ8S6TV+GlNL8kiH8rb4cm5Pb7A/C2BpyM/fA8ZJEudlCwi/jvAY+Ow==", - "dev": true - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "renderkid": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.2.tgz", - "integrity": "sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg==", - "dev": true, - "requires": { - "css-select": "^1.1.0", - "dom-converter": "~0.2", - "htmlparser2": "~3.3.0", - "strip-ansi": "^3.0.0", - "utila": "^0.4.0" - } - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", - "dev": true - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - } - } - }, - "request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "request-promise-native": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", - "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", - "dev": true, - "requires": { - "request-promise-core": "1.1.2", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "resp-modifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", - "integrity": "sha1-sSTeXE+6/LpUH0j/pzlw9KpFa08=", - "dev": true, - "requires": { - "debug": "^2.2.0", - "minimatch": "^3.0.2" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", - "dev": true - }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", - "dev": true - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rsvp": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", - "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", - "dev": true - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "requires": { - "is-promise": "^2.1.0" - } - }, - "run-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", - "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", - "dev": true - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=" - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - } - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "scoped-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-1.0.0.tgz", - "integrity": "sha1-o0a7Gs1CB65wvXwMfKnlZra63bg=", - "dev": true - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selfsigned": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", - "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", - "dev": true, - "requires": { - "node-forge": "0.7.5" - } - }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true - }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true - }, - "semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "requires": { - "semver": "^5.3.0" - } - }, - "semver-intersect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", - "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", - "dev": true, - "requires": { - "semver": "^5.0.0" - } - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - }, - "dependencies": { - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", - "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==", - "dev": true - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - } - }, - "server-destroy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", - "integrity": "sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0=", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shelljs": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", - "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "simple-git": { - "version": "1.110.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.110.0.tgz", - "integrity": "sha512-UYY0rQkknk0P5eb+KW+03F4TevZ9ou0H+LoGaj7iiVgpnZH4wdj/HTViy/1tNNkmIPcmtxuBqXWiYt2YwlRKOQ==", - "dev": true, - "requires": { - "debug": "^4.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "simple-progress-webpack-plugin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/simple-progress-webpack-plugin/-/simple-progress-webpack-plugin-1.1.2.tgz", - "integrity": "sha512-bNQfb3qSqbtsfxg6d0dGechUUJH2lZqKG5+bj2aoJmEA0rSzcm+2JVfC2YgkDABfuGItZ/O5ttt6BssWZW4SNg==", - "dev": true, - "requires": { - "chalk": "2.3.x", - "figures": "2.0.x", - "log-update": "2.3.x" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", - "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dev": true, - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - } - } - }, - "sisteransi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", - "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "smart-buffer": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", - "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "socket.io": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", - "dev": true, - "requires": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.1.1", - "socket.io-parser": "~3.2.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "engine.io-client": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", - "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - }, - "socket.io-client": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", - "dev": true, - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", - "to-array": "0.1.4" - } - }, - "socket.io-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", - "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "isarray": "2.0.1" - } - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - } - } - }, - "socket.io-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", - "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", - "dev": true - }, - "socket.io-client": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz", - "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==", - "dev": true, - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.3.1", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.3.0", - "to-array": "0.1.4" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "socket.io-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", - "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "isarray": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - } - } - }, - "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "dev": true, - "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" - } - }, - "sockjs-client": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", - "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", - "dev": true, - "requires": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "socks": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz", - "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==", - "dev": true, - "requires": { - "ip": "^1.1.5", - "smart-buffer": "4.0.2" - } - }, - "socks-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", - "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", - "dev": true, - "requires": { - "agent-base": "~4.2.0", - "socks": "~2.2.0" - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", - "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "sourcemap-codec": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz", - "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==", - "dev": true - }, - "spawn-sync": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", - "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", - "dev": true, - "requires": { - "concat-stream": "^1.4.7", - "os-shim": "^0.1.2" - } - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", - "dev": true - }, - "spdy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", - "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "dev": true - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "dev": true - }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "dev": true - }, - "stackframe": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz", - "integrity": "sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw==", - "dev": true - }, - "staged-git-files": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-1.1.2.tgz", - "integrity": "sha512-0Eyrk6uXW6tg9PYkhi/V/J4zHp33aNyi2hOCmhFLqLTIhbgqWn5jlSzI+IU0VqrZq6+DbHcabQl/WP6P3BG0QA==", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", - "dev": true - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "stream-throttle": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", - "integrity": "sha1-rdV8jXzHOoFjDTHNVdOWHPr7qcM=", - "dev": true, - "requires": { - "commander": "^2.2.0", - "limiter": "^1.0.5" - } - }, - "streamfilter": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/streamfilter/-/streamfilter-1.0.7.tgz", - "integrity": "sha512-Gk6KZM+yNA1JpW0KzlZIhjo3EaBJDkYfXtYSbOwNIQ7Zd6006E6+sCFlW1NDvFG/vnXhKmw6TJJgiEQg/8lXfQ==", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "string-argv": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.0.2.tgz", - "integrity": "sha1-2sMECGkMIfPDYwo/86BYd73L1zY=", - "dev": true - }, - "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", - "dev": true, - "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-bom-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", - "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", - "dev": true, - "requires": { - "first-chunk-stream": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - } - } - }, - "strip-comments": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz", - "integrity": "sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw==", - "dev": true, - "requires": { - "babel-extract-comments": "^1.0.0", - "babel-plugin-transform-object-rest-spread": "^6.26.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true - }, - "style-loader": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", - "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0" - } - }, - "stylehacks": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, - "svgo": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.2.0.tgz", - "integrity": "sha512-xBfxJxfk4UeVN8asec9jNxHiv3UAMv/ujwBWGYvQhhMb2u3YTGKkiybPcLFDLq7GLLWE9wa73e0/m8L5nTzQbw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.28", - "css-url-regex": "^1.1.0", - "csso": "^3.5.1", - "js-yaml": "^3.12.0", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "css-select": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz", - "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^2.1.2", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "swagger-ui": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/swagger-ui/-/swagger-ui-2.2.10.tgz", - "integrity": "sha1-sl56IWZOXZC/OR2zDbCN5B6FLXs=" - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, - "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", - "dev": true - }, - "synchronous-promise": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.7.tgz", - "integrity": "sha512-16GbgwTmFMYFyQMLvtQjvNWh30dsFe1cAW5Fg1wm5+dg84L9Pe36mftsIRU95/W2YsISxsz/xq4VB23sqpgb/A==", - "dev": true - }, - "tabtab": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tabtab/-/tabtab-2.2.2.tgz", - "integrity": "sha1-egR/FDsBC0y9MfhX6ClhUSy/ThQ=", - "dev": true, - "requires": { - "debug": "^2.2.0", - "inquirer": "^1.0.2", - "lodash.difference": "^4.5.0", - "lodash.uniq": "^4.5.0", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "npmlog": "^2.0.3", - "object-assign": "^4.1.0" - }, - "dependencies": { - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "external-editor": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz", - "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=", - "dev": true, - "requires": { - "extend": "^3.0.0", - "spawn-sync": "^1.0.15", - "tmp": "^0.0.29" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "inquirer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.2.3.tgz", - "integrity": "sha1-TexvMvN+97sLLtPx0aXD9UUHSRg=", - "dev": true, - "requires": { - "ansi-escapes": "^1.1.0", - "chalk": "^1.0.0", - "cli-cursor": "^1.0.1", - "cli-width": "^2.0.0", - "external-editor": "^1.1.0", - "figures": "^1.3.5", - "lodash": "^4.3.0", - "mute-stream": "0.0.6", - "pinkie-promise": "^2.0.0", - "run-async": "^2.2.0", - "rx": "^4.1.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "mute-stream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz", - "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=", - "dev": true - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "tmp": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz", - "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.1" - } - } - } - }, - "tapable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", - "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", - "dev": true - }, - "tar": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", - "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "terser": { - "version": "3.16.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz", - "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==", - "dev": true, - "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1", - "source-map-support": "~0.5.9" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "terser-webpack-plugin": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.2.tgz", - "integrity": "sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg==", - "dev": true, - "requires": { - "cacache": "^11.0.2", - "find-cache-dir": "^2.0.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "terser": "^3.16.1", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - }, - "dependencies": { - "find-cache-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz", - "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "test-exclude": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz", - "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^1.0.1" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - } - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "textextensions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.4.0.tgz", - "integrity": "sha512-qftQXnX1DzpSV8EddtHIT0eDDEiBF8ywhFYR2lI9xrGtxqKN+CvLXhACeCIGbCpQfxxERbrkZEFb8cZcDKbVZA==", - "dev": true - }, - "tfunk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tfunk/-/tfunk-3.1.0.tgz", - "integrity": "sha1-OORBT8ZJd9h6/apy+sttKfgve1s=", - "dev": true, - "requires": { - "chalk": "^1.1.1", - "object-path": "^0.9.0" - } - }, - "thread-loader": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-2.1.2.tgz", - "integrity": "sha512-7xpuc9Ifg6WU+QYw/8uUqNdRwMD+N5gjwHKMqETrs96Qn+7BHwECpt2Brzr4HFlf4IAkZsayNhmGdbkBsTJ//w==", - "dev": true, - "requires": { - "loader-runner": "^2.3.1", - "loader-utils": "^1.1.0", - "neo-async": "^2.6.0" - } - }, - "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "thunky": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", - "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", - "dev": true - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "to-string-loader": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/to-string-loader/-/to-string-loader-1.1.5.tgz", - "integrity": "sha1-e3qheJG3u0lHp6Eb+wO1/enG5pU=", - "dev": true, - "requires": { - "loader-utils": "^0.2.16" - }, - "dependencies": { - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - } - } - }, - "topo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz", - "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - }, - "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "tree-kill": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", - "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", - "dev": true - }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", - "dev": true - }, - "ts-jest": { - "version": "23.1.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-23.1.4.tgz", - "integrity": "sha512-9rCSxbWfoZxxeXnSoEIzRNr9hDIQ8iEJAWmSRsWhDHDT8OeuGfURhJQUE8jtJlkyEygs6rngH8RYtHz9cfjmEA==", - "dev": true, - "requires": { - "closest-file-data": "^0.1.4", - "fs-extra": "6.0.1", - "json5": "^0.5.0", - "lodash": "^4.17.10" - }, - "dependencies": { - "fs-extra": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", - "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - } - } - }, - "ts-loader": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.3.3.tgz", - "integrity": "sha512-KwF1SplmOJepnoZ4eRIloH/zXL195F51skt7reEsS6jvDqzgc/YSbz9b8E07GxIUwLXdcD4ssrJu6v8CwaTafA==", - "dev": true, - "requires": { - "chalk": "^2.3.0", - "enhanced-resolve": "^4.0.0", - "loader-utils": "^1.0.2", - "micromatch": "^3.1.4", - "semver": "^5.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" - }, - "tslint": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz", - "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==", - "dev": true, - "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.7.0", - "minimatch": "^3.0.4", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.27.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "tslint-config-prettier": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz", - "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==", - "dev": true - }, - "tslint-loader": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/tslint-loader/-/tslint-loader-3.6.0.tgz", - "integrity": "sha512-Me9Qf/87BOfCY8uJJw+J7VMF4U8WiMXKLhKKKugMydF0xMhMOt9wo2mjYTNhwbF9H7SHh8PAIwRG8roisTNekQ==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.1", - "rimraf": "^2.4.4", - "semver": "^5.3.0" - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", - "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==", - "dev": true - }, - "ua-parser-js": { - "version": "0.7.17", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", - "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==", - "dev": true - }, - "uglify-js": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", - "dev": true, - "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", - "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "untildify": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz", - "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==", - "dev": true - }, - "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", - "dev": true - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-parse": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", - "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", - "dev": true, - "requires": { - "querystringify": "^2.0.0", - "requires-port": "^1.0.0" - } - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true, - "requires": { - "prepend-http": "^1.0.1" - } - }, - "url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", - "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", - "dev": true, - "requires": { - "builtins": "^1.0.3" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "vendors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", - "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", - "dev": true, - "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - } - }, - "vinyl-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", - "integrity": "sha1-p+v1/779obfRjRQPyweyI++2dRo=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.3.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0", - "strip-bom-stream": "^2.0.0", - "vinyl": "^1.1.0" - }, - "dependencies": { - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - } - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "w3c-hr-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", - "dev": true, - "requires": { - "browser-process-hrtime": "^0.1.2" - } - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, - "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "dev": true, - "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "webpack": { - "version": "4.29.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.3.tgz", - "integrity": "sha512-xPJvFeB+8tUflXFq+OgdpiSnsCD5EANyv56co5q8q8+YtEasn5Sj3kzY44mta+csCIEB0vneSxnuaHkOL2h94A==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-module-context": "1.7.11", - "@webassemblyjs/wasm-edit": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11", - "acorn": "^6.0.5", - "acorn-dynamic-import": "^4.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^1.0.0", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.0", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "micromatch": "^3.1.8", - "mkdirp": "~0.5.0", - "neo-async": "^2.5.0", - "node-libs-browser": "^2.0.0", - "schema-utils": "^1.0.0", - "tapable": "^1.1.0", - "terser-webpack-plugin": "^1.1.0", - "watchpack": "^1.5.0", - "webpack-sources": "^1.3.0" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - } - } - }, - "webpack-cli": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.2.3.tgz", - "integrity": "sha512-Ik3SjV6uJtWIAN5jp5ZuBMWEAaP5E4V78XJ2nI+paFPh8v4HPSwo/myN0r29Xc/6ZKnd2IdrAlpSgNOu2CDQ6Q==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "enhanced-resolve": "^4.1.0", - "findup-sync": "^2.0.0", - "global-modules": "^1.0.0", - "import-local": "^2.0.0", - "interpret": "^1.1.0", - "loader-utils": "^1.1.0", - "supports-color": "^5.5.0", - "v8-compile-cache": "^2.0.2", - "yargs": "^12.0.4" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "camelcase": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.0.tgz", - "integrity": "sha512-Y05ICatFYPAfykDIB7VdwSJ0LUl1yq/BwO2OpyGGLjiRe1fgzTwVypPiWnzkGFOVFHXrCXUNBl86bpjBhZWSJg==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "webpack-dev-middleware": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", - "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", - "dev": true, - "requires": { - "memory-fs": "~0.4.1", - "mime": "^2.3.1", - "range-parser": "^1.0.3", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", - "dev": true - } - } - }, - "webpack-dev-server": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz", - "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.0.0", - "compression": "^1.5.2", - "connect-history-api-fallback": "^1.3.0", - "debug": "^3.1.0", - "del": "^3.0.0", - "express": "^4.16.2", - "html-entities": "^1.2.0", - "http-proxy-middleware": "~0.18.0", - "import-local": "^2.0.0", - "internal-ip": "^3.0.1", - "ip": "^1.1.5", - "killable": "^1.0.0", - "loglevel": "^1.4.1", - "opn": "^5.1.0", - "portfinder": "^1.0.9", - "schema-utils": "^1.0.0", - "selfsigned": "^1.9.1", - "semver": "^5.6.0", - "serve-index": "^1.7.2", - "sockjs": "0.3.19", - "sockjs-client": "1.3.0", - "spdy": "^4.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^5.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "3.4.0", - "webpack-log": "^2.0.0", - "yargs": "12.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", - "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", - "dev": true, - "requires": { - "xregexp": "4.0.0" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "yargs": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", - "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^2.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" - } - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } - } - }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "dev": true, - "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - } - }, - "webpack-merge": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.1.tgz", - "integrity": "sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==", - "dev": true, - "requires": { - "lodash": "^4.17.5" - } - }, - "webpack-notifier": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/webpack-notifier/-/webpack-notifier-1.7.0.tgz", - "integrity": "sha512-L3UKrl500xk0VDYKkwQxy5/BPhBWsZ2xHsAx2Qe3dVKYUEk9+y690RcNTMIUcVOK2fRgK7KK3PA4ccOq1h+fTg==", - "dev": true, - "requires": { - "node-notifier": "^5.1.2", - "object-assign": "^4.1.0", - "strip-ansi": "^3.0.1" - } - }, - "webpack-sources": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", - "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "webpack-visualizer-plugin": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/webpack-visualizer-plugin/-/webpack-visualizer-plugin-0.1.11.tgz", - "integrity": "sha1-uHcK2GtPZSYSxosbeCJT+vn4o04=", - "dev": true, - "requires": { - "d3": "^3.5.6", - "mkdirp": "^0.5.1", - "react": "^0.14.0", - "react-dom": "^0.14.0" - } - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", - "dev": true, - "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-fetch": { - "version": "0.9.0", - "resolved": "http://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz", - "integrity": "sha1-DjaExsuZlbQ+/J3wPkw2XZX9nMA=", - "dev": true - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "win-release": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz", - "integrity": "sha1-X6VeAr58qTTt/BJmVjLoSbcuUgk=", - "dev": true, - "requires": { - "semver": "^5.0.1" - } - }, - "window-size": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", - "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=", - "dev": true - }, - "winston": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", - "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", - "dev": true, - "requires": { - "async": "^2.6.1", - "diagnostics": "^1.1.1", - "is-stream": "^1.1.0", - "logform": "^2.1.1", - "one-time": "0.0.4", - "readable-stream": "^3.1.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.3.0" - }, - "dependencies": { - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "winston-transport": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", - "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", - "dev": true, - "requires": { - "readable-stream": "^2.3.6", - "triple-beam": "^1.2.0" - } - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - }, - "workbox-background-sync": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-3.6.3.tgz", - "integrity": "sha512-ypLo0B6dces4gSpaslmDg5wuoUWrHHVJfFWwl1udvSylLdXvnrfhFfriCS42SNEe5lsZtcNZF27W/SMzBlva7Q==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-broadcast-cache-update": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-broadcast-cache-update/-/workbox-broadcast-cache-update-3.6.3.tgz", - "integrity": "sha512-pJl4lbClQcvp0SyTiEw0zLSsVYE1RDlCPtpKnpMjxFtu8lCFTAEuVyzxp9w7GF4/b3P4h5nyQ+q7V9mIR7YzGg==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-build": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-3.6.3.tgz", - "integrity": "sha512-w0clZ/pVjL8VXy6GfthefxpEXs0T8uiRuopZSFVQ8ovfbH6c6kUpEh6DcYwm/Y6dyWPiCucdyAZotgjz+nRz8g==", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "common-tags": "^1.4.0", - "fs-extra": "^4.0.2", - "glob": "^7.1.2", - "joi": "^11.1.1", - "lodash.template": "^4.4.0", - "pretty-bytes": "^4.0.2", - "stringify-object": "^3.2.2", - "strip-comments": "^1.0.2", - "workbox-background-sync": "^3.6.3", - "workbox-broadcast-cache-update": "^3.6.3", - "workbox-cache-expiration": "^3.6.3", - "workbox-cacheable-response": "^3.6.3", - "workbox-core": "^3.6.3", - "workbox-google-analytics": "^3.6.3", - "workbox-navigation-preload": "^3.6.3", - "workbox-precaching": "^3.6.3", - "workbox-range-requests": "^3.6.3", - "workbox-routing": "^3.6.3", - "workbox-strategies": "^3.6.3", - "workbox-streams": "^3.6.3", - "workbox-sw": "^3.6.3" - }, - "dependencies": { - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "pretty-bytes": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", - "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=", - "dev": true - } - } - }, - "workbox-cache-expiration": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-cache-expiration/-/workbox-cache-expiration-3.6.3.tgz", - "integrity": "sha512-+ECNph/6doYx89oopO/UolYdDmQtGUgo8KCgluwBF/RieyA1ZOFKfrSiNjztxOrGJoyBB7raTIOlEEwZ1LaHoA==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-cacheable-response": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-3.6.3.tgz", - "integrity": "sha512-QpmbGA9SLcA7fklBLm06C4zFg577Dt8u3QgLM0eMnnbaVv3rhm4vbmDpBkyTqvgK/Ly8MBDQzlXDtUCswQwqqg==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-core": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-3.6.3.tgz", - "integrity": "sha512-cx9cx0nscPkIWs8Pt98HGrS9/aORuUcSkWjG25GqNWdvD/pSe7/5Oh3BKs0fC+rUshCiyLbxW54q0hA+GqZeSQ==", - "dev": true - }, - "workbox-google-analytics": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-3.6.3.tgz", - "integrity": "sha512-RQBUo/6SXtIaQTRFj4RQZ9e1gAl7D8oS5S+Hi173Kk70/BgJjzPwXpC5A249Jv5YfkCOLMQCeF9A27BiD0b0ig==", - "dev": true, - "requires": { - "workbox-background-sync": "^3.6.3", - "workbox-core": "^3.6.3", - "workbox-routing": "^3.6.3", - "workbox-strategies": "^3.6.3" - } - }, - "workbox-navigation-preload": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-3.6.3.tgz", - "integrity": "sha512-dd26xTX16DUu0i+MhqZK/jQXgfIitu0yATM4jhRXEmpMqQ4MxEeNvl2CgjDMOHBnCVMax+CFZQWwxMx/X/PqCw==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-precaching": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-3.6.3.tgz", - "integrity": "sha512-aBqT66BuMFviPTW6IpccZZHzpA8xzvZU2OM1AdhmSlYDXOJyb1+Z6blVD7z2Q8VNtV1UVwQIdImIX+hH3C3PIw==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-range-requests": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-3.6.3.tgz", - "integrity": "sha512-R+yLWQy7D9aRF9yJ3QzwYnGFnGDhMUij4jVBUVtkl67oaVoP1ymZ81AfCmfZro2kpPRI+vmNMfxxW531cqdx8A==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-routing": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-3.6.3.tgz", - "integrity": "sha512-bX20i95OKXXQovXhFOViOK63HYmXvsIwZXKWbSpVeKToxMrp0G/6LZXnhg82ijj/S5yhKNRf9LeGDzaqxzAwMQ==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-strategies": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-3.6.3.tgz", - "integrity": "sha512-Pg5eulqeKet2y8j73Yw6xTgLdElktcWExGkzDVCGqfV9JCvnGuEpz5eVsCIK70+k4oJcBCin9qEg3g3CwEIH3g==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-streams": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-3.6.3.tgz", - "integrity": "sha512-rqDuS4duj+3aZUYI1LsrD2t9hHOjwPqnUIfrXSOxSVjVn83W2MisDF2Bj+dFUZv4GalL9xqErcFW++9gH+Z27w==", - "dev": true, - "requires": { - "workbox-core": "^3.6.3" - } - }, - "workbox-sw": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-3.6.3.tgz", - "integrity": "sha512-IQOUi+RLhvYCiv80RP23KBW/NTtIvzvjex28B8NW1jOm+iV4VIu3VXKXTA6er5/wjjuhmtB28qEAUqADLAyOSg==", - "dev": true - }, - "workbox-webpack-plugin": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-3.6.3.tgz", - "integrity": "sha512-RwmKjc7HFHUFHoOlKoZUq9349u0QN3F8W5tZZU0vc1qsBZDINWXRiIBCAKvo/Njgay5sWz7z4I2adnyTo97qIQ==", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "json-stable-stringify": "^1.0.1", - "workbox-build": "^3.6.3" - } - }, - "worker-farm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write-file-atomic": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", - "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "write-file-webpack-plugin": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/write-file-webpack-plugin/-/write-file-webpack-plugin-4.5.0.tgz", - "integrity": "sha512-k46VeERtaezbmjpDcMWATjKUWBrVe/ZEEm0cyvUm8FFP8A/r+dw5x3psRvkUOhqh9bqBLUlGYYbtr6luI+HeAg==", - "dev": true, - "requires": { - "chalk": "^2.4.0", - "debug": "^3.1.0", - "filesize": "^3.6.1", - "lodash": "^4.17.5", - "mkdirp": "^0.5.1", - "moment": "^2.22.1", - "write-file-atomic": "^2.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "ws": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.3.tgz", - "integrity": "sha512-tbSxiT+qJI223AP4iLfQbkbxkwdFcneYinM2+x46Gx2wgvbaOMO36czfdfVUBRTHvzAMRhDd98sA5d/BuWbQdg==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, - "xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", - "dev": true - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", - "dev": true - }, - "xregexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", - "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true - }, - "yargs": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", - "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - }, - "dependencies": { - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, - "yeoman-environment": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-2.3.0.tgz", - "integrity": "sha512-PHSAkVOqYdcR+C+Uht1SGC4eVD/9OhygYFkYaI66xF8vKIeS1RNYay+umj2ZrQeJ50tF5Q/RSO6qGDz9y3Ifug==", - "dev": true, - "requires": { - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^3.1.0", - "diff": "^3.3.1", - "escape-string-regexp": "^1.0.2", - "globby": "^8.0.1", - "grouped-queue": "^0.3.3", - "inquirer": "^5.2.0", - "is-scoped": "^1.0.0", - "lodash": "^4.17.10", - "log-symbols": "^2.1.0", - "mem-fs": "^1.1.0", - "strip-ansi": "^4.0.0", - "text-table": "^0.2.0", - "untildify": "^3.0.2" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "path-type": "^3.0.0" - } - }, - "globby": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", - "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "dir-glob": "2.0.0", - "fast-glob": "^2.0.2", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - } - }, - "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", - "dev": true, - "requires": { - "symbol-observable": "1.0.1" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - } - } - }, - "yeoman-generator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-3.0.0.tgz", - "integrity": "sha512-aHsNXzkdgAoakZTZsDX7T56wYWYd1O5E/GBIFAVMJLH7TKRr+1MiEJszZQbbCSA+J+lpT743/8L88j35yNdTLQ==", - "dev": true, - "requires": { - "async": "^2.6.0", - "chalk": "^2.3.0", - "cli-table": "^0.3.1", - "cross-spawn": "^6.0.5", - "dargs": "^6.0.0", - "dateformat": "^3.0.3", - "debug": "^3.1.0", - "detect-conflict": "^1.0.0", - "error": "^7.0.2", - "find-up": "^3.0.0", - "github-username": "^4.0.0", - "istextorbinary": "^2.2.1", - "lodash": "^4.17.10", - "make-dir": "^1.1.0", - "mem-fs-editor": "^5.0.0", - "minimist": "^1.2.0", - "pretty-bytes": "^5.1.0", - "read-chunk": "^2.1.0", - "read-pkg-up": "^4.0.0", - "rimraf": "^2.6.2", - "run-async": "^2.0.0", - "shelljs": "^0.8.0", - "text-table": "^0.2.0", - "through2": "^2.0.0", - "yeoman-environment": "^2.0.5" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "yup": { - "version": "0.26.10", - "resolved": "https://registry.npmjs.org/yup/-/yup-0.26.10.tgz", - "integrity": "sha512-keuNEbNSnsOTOuGCt3UJW69jDE3O4P+UHAakO7vSeFMnjaitcmlbij/a3oNb9g1Y1KvSKH/7O1R2PQ4m4TRylw==", - "dev": true, - "requires": { - "@babel/runtime": "7.0.0", - "fn-name": "~2.0.1", - "lodash": "^4.17.10", - "property-expr": "^1.5.0", - "synchronous-promise": "^2.0.5", - "toposort": "^2.0.2" - }, - "dependencies": { - "toposort": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=", - "dev": true - } - } - }, - "zone.js": { - "version": "0.8.29", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.29.tgz", - "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 63f594d6..00000000 --- a/package.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "name": "hsadmin-ng", - "version": "0.0.0", - "description": "Description for hsadminNg", - "private": true, - "license": "UNLICENSED", - "cacheDirectories": [ - "node_modules" - ], - "dependencies": { - "@angular/common": "7.2.4", - "@angular/compiler": "7.2.4", - "@angular/core": "7.2.4", - "@angular/forms": "7.2.4", - "@angular/platform-browser": "7.2.4", - "@angular/platform-browser-dynamic": "7.2.4", - "@angular/router": "7.2.4", - "@fortawesome/angular-fontawesome": "0.3.0", - "@fortawesome/fontawesome-svg-core": "1.2.14", - "@fortawesome/free-solid-svg-icons": "5.7.1", - "@ng-bootstrap/ng-bootstrap": "4.0.2", - "@ngx-translate/core": "11.0.1", - "@ngx-translate/http-loader": "4.0.0", - "bootstrap": "4.2.1", - "core-js": "2.6.4", - "moment": "2.24.0", - "ng-jhipster": "0.9.1", - "ngx-cookie": "2.0.1", - "ngx-infinite-scroll": "7.0.1", - "ngx-webstorage": "2.0.1", - "rxjs": "6.4.0", - "swagger-ui": "2.2.10", - "tslib": "1.9.3", - "zone.js": "0.8.29" - }, - "devDependencies": { - "@angular/cli": "7.3.1", - "@angular/compiler-cli": "7.2.4", - "@ngtools/webpack": "7.3.1", - "@types/jest": "24.0.0", - "@types/node": "10.12.24", - "angular-router-loader": "0.8.5", - "angular2-template-loader": "0.6.2", - "autoprefixer": "9.4.7", - "browser-sync": "2.26.3", - "browser-sync-webpack-plugin": "2.2.2", - "cache-loader": "2.0.1", - "codelyzer": "4.5.0", - "copy-webpack-plugin": "4.6.0", - "css-loader": "2.1.0", - "file-loader": "3.0.1", - "fork-ts-checker-webpack-plugin": "0.5.2", - "friendly-errors-webpack-plugin": "1.7.0", - "generator-jhipster": "5.8.2", - "html-loader": "0.5.5", - "html-webpack-plugin": "3.2.0", - "husky": "1.3.1", - "jest": "24.1.0", - "jest-junit": "6.2.1", - "jest-preset-angular": "6.0.2", - "jest-sonar-reporter": "2.0.0", - "lint-staged": "8.1.3", - "merge-jsons-webpack-plugin": "1.0.18", - "mini-css-extract-plugin": "0.5.0", - "moment-locales-webpack-plugin": "1.0.7", - "optimize-css-assets-webpack-plugin": "5.0.1", - "prettier": "1.16.4", - "reflect-metadata": "0.1.13", - "rimraf": "2.6.3", - "simple-progress-webpack-plugin": "1.1.2", - "style-loader": "0.23.1", - "terser-webpack-plugin": "1.2.2", - "thread-loader": "2.1.2", - "to-string-loader": "1.1.5", - "ts-loader": "5.3.3", - "tslint": "5.12.1", - "tslint-config-prettier": "1.18.0", - "tslint-loader": "3.6.0", - "typescript": "3.2.4", - "postcss-loader": "3.0.0", - "webpack": "4.29.3", - "webpack-cli": "3.2.3", - "webpack-dev-server": "3.1.14", - "webpack-merge": "4.2.1", - "webpack-notifier": "1.7.0", - "webpack-visualizer-plugin": "0.1.11", - "workbox-webpack-plugin": "3.6.3", - "write-file-webpack-plugin": "4.5.0" - }, - "engines": { - "node": ">=8.9.0" - }, - "lint-staged": { - "{,src/**/}*.{md,json,ts,css,scss}": [ - "prettier --write", - "git add" - ] - }, - "scripts": { - "prettier:format": "prettier --write \"{,src/**/}*.{md,json,ts,css,scss}\"", - "lint": "tslint --project tsconfig.json -e 'node_modules/**'", - "lint:fix": "npm run lint -- --fix", - "ngc": "ngc -p tsconfig-aot.json", - "cleanup": "rimraf build/{aot,www}", - "clean-www": "rimraf build//www/app/{src,build/}", - "start": "npm run webpack:dev", - "start-tls": "npm run webpack:dev -- --env.tls", - "serve": "npm run start", - "build": "npm run webpack:prod", - "test": "npm run lint && jest --coverage --logHeapUsage -w=2 --config src/test/javascript/jest.conf.js", - "test:watch": "npm run test -- --watch", - "webpack:dev": "npm run webpack-dev-server -- --config webpack/webpack.dev.js --inline --hot --port=9060 --watch-content-base --env.stats=minimal", - "webpack:dev-verbose": "npm run webpack-dev-server -- --config webpack/webpack.dev.js --inline --hot --port=9060 --watch-content-base --profile --progress --env.stats=normal", - "webpack:build:main": "npm run webpack -- --config webpack/webpack.dev.js --env.stats=minimal", - "webpack:build": "npm run cleanup && npm run webpack:build:main", - "webpack:prod:main": "npm run webpack -- --config webpack/webpack.prod.js --profile", - "webpack:prod": "npm run cleanup && npm run webpack:prod:main && npm run clean-www", - "webpack:test": "npm run test", - "webpack-dev-server": "node --max_old_space_size=4096 node_modules/webpack-dev-server/bin/webpack-dev-server.js", - "webpack": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack.js" - }, - "jestSonar": { - "reportPath": "build/test-results/jest", - "reportFile": "TESTS-results-sonar.xml" - } -} diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index a26de7e9..00000000 --- a/postcss.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - plugins: [ - require('autoprefixer') - ] -} diff --git a/proxy.conf.json b/proxy.conf.json deleted file mode 100644 index 8b41fdf7..00000000 --- a/proxy.conf.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "*": { - "target": "http://localhost:8080", - "secure": false, - "loglevel": "debug" - } -} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 285ecb0c..00000000 --- a/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'hsadmin-ng' diff --git a/sql/00-util.sql b/sql/00-util.sql new file mode 100644 index 00000000..79326344 --- /dev/null +++ b/sql/00-util.sql @@ -0,0 +1,38 @@ +abort; +set local session authorization default; + + +CREATE OR REPLACE FUNCTION array_distinct(anyarray) RETURNS anyarray AS $f$ +SELECT array_agg(DISTINCT x) FROM unnest($1) t(x); +$f$ LANGUAGE SQL IMMUTABLE; + + +CREATE OR REPLACE FUNCTION lastRowCount() + RETURNS bigint + LANGUAGE plpgsql AS $$ +DECLARE + lastRowCount bigint; +BEGIN + GET DIAGNOSTICS lastRowCount = ROW_COUNT; + RETURN lastRowCount; +END; +$$; + +-- ======================================================== +-- Test Data helpers +-- -------------------------------------------------------- + +CREATE OR REPLACE FUNCTION intToVarChar(i integer, len integer) + RETURNS varchar + LANGUAGE plpgsql AS $$ +DECLARE +partial varchar; +BEGIN +SELECT chr(ascii('a') + i%26) INTO partial; +IF len > 1 THEN + RETURN intToVarChar(i/26, len-1) || partial; +ELSE + RETURN partial; +END IF; +END; $$; +SELECT * FROM intToVarChar(211, 4); diff --git a/sql/10-rbac-base.sql b/sql/10-rbac-base.sql new file mode 100644 index 00000000..1f783077 --- /dev/null +++ b/sql/10-rbac-base.sql @@ -0,0 +1,657 @@ + +-- ======================================================== +-- RBAC +-- -------------------------------------------------------- + +SET SESSION SESSION AUTHORIZATION DEFAULT; + +-- https://arctype.com/blog/postgres-uuid/#creating-a-uuid-primary-key-using-uuid-osp-postgresql-example +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +DROP TABLE IF EXISTS "RbacPermission"; +DROP TABLE IF EXISTS "RbacGrants"; +DROP TABLE IF EXISTS "RbacUser"; +DROP TABLE IF EXISTS RbacReference CASCADE; +DROP TYPE IF EXISTS RbacOp CASCADE; +DROP TYPE IF EXISTS ReferenceType CASCADE; + +CREATE TYPE ReferenceType AS ENUM ('RbacUser', 'RbacRole', 'RbacPermission'); + +CREATE TABLE RbacReference +( + uuid uuid UNIQUE DEFAULT uuid_generate_v4(), + type ReferenceType not null +); + +CREATE TABLE RbacUser +( + uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, + name varchar(63) not null unique +); + +CREATE TABLE RbacRole +( + uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, + name varchar(63) not null unique +); + +CREATE TABLE RbacGrants +( + ascendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, + descendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, + -- apply bool not null, -- alternative 1 to implement assumable roles + primary key (ascendantUuid, descendantUuid) +); +CREATE INDEX ON RbacGrants (ascendantUuid); +CREATE INDEX ON RbacGrants (descendantUuid); + +DROP DOMAIN IF EXISTS RbacOp CASCADE; +CREATE DOMAIN RbacOp AS VARCHAR(67) + CHECK( + VALUE = '*' + OR VALUE = 'delete' + OR VALUE = 'edit' + OR VALUE = 'view' + OR VALUE = 'assume' + OR VALUE ~ '^add-[a-z]+$' + ); + +DROP TABLE IF EXISTS RbacObject; +CREATE TABLE RbacObject +( + uuid uuid UNIQUE DEFAULT uuid_generate_v4(), + objectTable varchar(64) not null, + unique (objectTable, uuid) +); + +CREATE OR REPLACE FUNCTION createRbacObject() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ +DECLARE + objectUuid uuid; +BEGIN + IF TG_OP = 'INSERT' THEN + INSERT INTO RbacObject (objectTable) VALUES (TG_TABLE_NAME) RETURNING uuid INTO objectUuid; + NEW.uuid = objectUuid; + RETURN NEW; + ELSE + RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; + END IF; +END; $$; + + +DROP TABLE IF EXISTS RbacPermission; +CREATE TABLE RbacPermission +( uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, + objectUuid uuid not null, + op RbacOp not null, + unique (objectUuid, op) +); + +CREATE OR REPLACE FUNCTION hasPermission(forObjectUuid uuid, forOp RbacOp) + RETURNS bool + LANGUAGE sql AS $$ + SELECT EXISTS ( + SELECT op + FROM RbacPermission p + WHERE p.objectUuid=forObjectUuid AND p.op in ('*', forOp) + ); + $$; + +CREATE OR REPLACE FUNCTION createRbacUser(userName varchar) + RETURNS uuid + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +declare + objectId uuid; +BEGIN + INSERT INTO RbacReference (type) VALUES ('RbacUser') RETURNING uuid INTO objectId; + INSERT INTO RbacUser (uuid, name) VALUES (objectid, userName); + return objectId; +END; +$$; + +CREATE OR REPLACE FUNCTION findRbacUser(userName varchar) -- TODO: rename to ...Id + RETURNS uuid + RETURNS NULL ON NULL INPUT + LANGUAGE sql AS $$ + SELECT uuid FROM RbacUser WHERE name = userName +$$; + +CREATE OR REPLACE FUNCTION getRbacUserId(userName varchar, whenNotExists RbacWhenNotExists) + RETURNS uuid + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +DECLARE + userUuid uuid; +BEGIN + userUuid = findRbacUser(userName); + IF ( userUuid IS NULL ) THEN + IF ( whenNotExists = 'fail') THEN + RAISE EXCEPTION 'RbacUser with name="%" not found', userName; + END IF; + IF ( whenNotExists = 'create') THEN + userUuid = createRbacUser(userName); + END IF; + END IF; + return userUuid; +END; +$$; + +CREATE OR REPLACE FUNCTION createRole(roleName varchar) + RETURNS uuid + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +declare + referenceId uuid; +BEGIN + INSERT INTO RbacReference (type) VALUES ('RbacRole') RETURNING uuid INTO referenceId; + INSERT INTO RbacRole (uuid, name) VALUES (referenceId, roleName); + IF (referenceId IS NULL) THEN + RAISE EXCEPTION 'referenceId for roleName "%" is unexpectedly null', roleName; + end if; + return referenceId; +END; +$$; + + +CREATE OR REPLACE PROCEDURE deleteRole(roleUUid uuid) + LANGUAGE plpgsql AS $$ +BEGIN + DELETE FROM RbacRole WHERE uuid=roleUUid; +END; +$$; + +CREATE OR REPLACE FUNCTION findRoleId(roleName varchar) + RETURNS uuid + RETURNS NULL ON NULL INPUT + LANGUAGE sql AS $$ + SELECT uuid FROM RbacRole WHERE name = roleName +$$; + +CREATE TYPE RbacWhenNotExists AS ENUM ('fail', 'create'); + +CREATE OR REPLACE FUNCTION getRoleId(roleName varchar, whenNotExists RbacWhenNotExists) + RETURNS uuid + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +DECLARE + roleUuid uuid; +BEGIN + roleUuid = findRoleId(roleName); + IF ( roleUuid IS NULL ) THEN + IF ( whenNotExists = 'fail') THEN + RAISE EXCEPTION 'RbacRole with name="%" not found', roleName; + END IF; + IF ( whenNotExists = 'create') THEN + roleUuid = createRole(roleName); + END IF; + END IF; + return roleUuid; +END; +$$; + +-- select getRoleId('hostmaster', 'create'); + +CREATE OR REPLACE FUNCTION createPermissions(forObjectUuid uuid, permitOps RbacOp[]) + RETURNS uuid[] + LANGUAGE plpgsql AS $$ +DECLARE + refId uuid; + permissionIds uuid[] = ARRAY[]::uuid[]; +BEGIN + IF ( forObjectUuid IS NULL ) THEN + RAISE EXCEPTION 'forObjectUuid must not be null'; + END IF; + IF ( array_length(permitOps, 1) > 1 AND '*' = any(permitOps) ) THEN + RAISE EXCEPTION '"*" operation must not be assigned along with other operations: %', permitOps; + END IF; + + FOR i IN array_lower(permitOps, 1)..array_upper(permitOps, 1) LOOP + refId = (SELECT uuid FROM RbacPermission WHERE objectUuid=forObjectUuid AND op=permitOps[i]); + IF (refId IS NULL) THEN + INSERT INTO RbacReference ("type") VALUES ('RbacPermission') RETURNING uuid INTO refId; + INSERT INTO RbacPermission (uuid, objectUuid, op) VALUES (refId, forObjectUuid, permitOps[i]); + END IF; + permissionIds = permissionIds || refId; + END LOOP; + return permissionIds; +END; +$$; + +CREATE OR REPLACE FUNCTION findPermissionId(forObjectTable varchar, forObjectUuid uuid, forOp RbacOp) + RETURNS uuid + RETURNS NULL ON NULL INPUT + STABLE LEAKPROOF + LANGUAGE sql AS $$ + SELECT uuid FROM RbacPermission p + WHERE p.objectUuid=forObjectUuid AND p.op in ('*', forOp) +$$; + +CREATE OR REPLACE FUNCTION assertReferenceType(argument varchar, referenceId uuid, expectedType ReferenceType) + RETURNS ReferenceType + LANGUAGE plpgsql AS $$ +DECLARE + actualType ReferenceType; +BEGIN + actualType = (SELECT type FROM RbacReference WHERE uuid=referenceId); + IF ( actualType <> expectedType ) THEN + RAISE EXCEPTION '% must reference a %, but got a %', argument, expectedType, actualType; + end if; + RETURN expectedType; +END; $$; + +CREATE OR REPLACE PROCEDURE grantPermissionsToRole(roleUuid uuid, permissionIds uuid[]) + LANGUAGE plpgsql AS $$ +BEGIN + FOR i IN array_lower(permissionIds, 1)..array_upper(permissionIds, 1) LOOP + perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole'); + perform assertReferenceType('permissionId (descendant)', permissionIds[i], 'RbacPermission'); + + -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (roleId, permissionIds[i], true); -- assumeV1 + INSERT INTO RbacGrants (ascendantUuid, descendantUuid) VALUES (roleUuid, permissionIds[i]); + END LOOP; +END; +$$; + +CREATE OR REPLACE PROCEDURE grantRoleToRole(subRoleId uuid, superRoleId uuid + -- , doapply bool = true -- assumeV1 + ) + LANGUAGE plpgsql AS $$ +BEGIN + perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole'); + perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole'); + + RAISE NOTICE 'granting subRole % to superRole %', subRoleId, superRoleId; -- TODO: remove + + IF ( isGranted(subRoleId, superRoleId) ) THEN + RAISE EXCEPTION 'Cyclic role grant detected between % and %', subRoleId, superRoleId; + END IF; + + -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (superRoleId, subRoleId, doapply); -- assumeV1 + INSERT INTO RbacGrants (ascendantUuid, descendantUuid) VALUES (superRoleId, subRoleId) + ON CONFLICT DO NOTHING ; -- TODO: remove +END; $$; + +CREATE OR REPLACE PROCEDURE revokeRoleFromRole(subRoleId uuid, superRoleId uuid) + LANGUAGE plpgsql AS $$ +BEGIN + perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole'); + perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole'); + + IF ( isGranted(subRoleId, superRoleId) ) THEN + DELETE FROM RbacGrants WHERE ascendantUuid=superRoleId AND descendantUuid=subRoleId; + END IF; +END; $$; + +CREATE OR REPLACE PROCEDURE grantRoleToUser(roleId uuid, userId uuid) + LANGUAGE plpgsql AS $$ +BEGIN + perform assertReferenceType('roleId (ascendant)', roleId, 'RbacRole'); + perform assertReferenceType('userId (descendant)', userId, 'RbacUser'); + + -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (userId, roleId, true); -- assumeV1 + INSERT INTO RbacGrants (ascendantUuid, descendantUuid) VALUES (userId, roleId) + ON CONFLICT DO NOTHING ; -- TODO: remove +END; $$; + +abort; +set local session authorization default; + +CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( + requiredOp RbacOp, + -- objectTable varchar, -- TODO: maybe another optimization? but test perforamance for joins! + subjectIds uuid[], + maxDepth integer = 8, + maxObjects integer = 16000) + RETURNS SETOF uuid + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ + DECLARE + foundRows bigint; + BEGIN + RETURN QUERY SELECT DISTINCT perm.objectUuid + FROM ( + WITH RECURSIVE grants AS ( + SELECT descendantUuid, ascendantUuid, 1 AS level + FROM RbacGrants + WHERE ascendantUuid = ANY(subjectIds) + UNION ALL + SELECT "grant".descendantUuid, "grant".ascendantUuid, level + 1 AS level + FROM RbacGrants "grant" + INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid + WHERE level <= maxDepth + ) SELECT descendantUuid + FROM grants + -- LIMIT maxObjects+1 + ) as granted + JOIN RbacPermission perm ON granted.descendantUuid=perm.uuid AND perm.op IN ('*', requiredOp); + + foundRows = lastRowCount(); + IF foundRows > maxObjects THEN + RAISE EXCEPTION 'Too many accessible objects, limit is %, found %.', maxObjects, foundRows + USING + ERRCODE = 'P0003', -- 'HS-ADMIN-NG:ACC-OBJ-EXC', + HINT = 'Please assume a sub-role and try again.'; + END IF; + END; +$$; + +abort; +set local session authorization restricted; +begin transaction; +set local statement_timeout TO '60s'; +select count(*) + from queryAccessibleObjectUuidsOfSubjectIds('view', ARRAY[findRbacUser('mike@hostsharing.net')], 4, 10000); +end transaction; + +--- + +abort; +set local session authorization default; +CREATE OR REPLACE FUNCTION queryRequiredPermissionsOfSubjectIds(requiredOp RbacOp, subjectIds uuid[]) + RETURNS SETOF RbacPermission + STRICT + LANGUAGE sql AS $$ + SELECT DISTINCT * + FROM RbacPermission + WHERE op = '*' OR op = requiredOp + AND uuid IN ( + WITH RECURSIVE grants AS ( + SELECT DISTINCT + descendantUuid, + ascendantUuid + FROM RbacGrants + WHERE + ascendantUuid = ANY(subjectIds) + UNION ALL + SELECT + "grant".descendantUuid, + "grant".ascendantUuid + FROM RbacGrants "grant" + INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid + ) SELECT + descendantUuid + FROM grants + ); +$$; + +abort; +set local session authorization restricted; +begin transaction; +-- set local statement_timeout TO '5s'; +set local statement_timeout TO '5min'; +select count(*) from queryRequiredPermissionsOfSubjectIds('view', ARRAY[findRbacUser('mike@hostsharing.net')]); +end transaction; + +--- + +abort; +set local session authorization default; +CREATE OR REPLACE FUNCTION queryAllPermissionsOfSubjectIds(subjectIds uuid[]) + RETURNS SETOF RbacPermission + STRICT + LANGUAGE sql AS $$ + SELECT DISTINCT * FROM RbacPermission WHERE uuid IN ( + WITH RECURSIVE grants AS ( + SELECT DISTINCT + descendantUuid, + ascendantUuid + FROM RbacGrants + WHERE + ascendantUuid = ANY(subjectIds) + UNION ALL + SELECT + "grant".descendantUuid, + "grant".ascendantUuid + FROM RbacGrants "grant" + INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid + ) SELECT + descendantUuid + FROM grants + ); + $$; + +abort; +set local session authorization restricted; +begin transaction; + set local statement_timeout TO '5s'; + select count(*) from queryAllPermissionsOfSubjectIds(ARRAY[findRbacUser('mike@hostsharing.net')]); +end transaction; + +--- + +CREATE OR REPLACE FUNCTION queryAllPermissionsOfSubjectId(subjectId uuid) -- TODO: remove? + RETURNS SETOF RbacPermission + RETURNS NULL ON NULL INPUT + LANGUAGE sql AS $$ + SELECT * FROM RbacPermission WHERE uuid IN ( + WITH RECURSIVE grants AS ( + SELECT + descendantUuid, + ascendantUuid + FROM + RbacGrants + WHERE + ascendantUuid = subjectId + UNION ALL + SELECT + "grant".descendantUuid, + "grant".ascendantUuid + FROM RbacGrants "grant" + INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid + ) SELECT + descendantUuid + FROM + grants + ); +$$; + +--- + +CREATE OR REPLACE FUNCTION queryAllRbacUsersWithPermissionsFor(objectId uuid) + RETURNS SETOF RbacUser + RETURNS NULL ON NULL INPUT + LANGUAGE sql AS $$ +SELECT * FROM RbacUser WHERE uuid IN ( + WITH RECURSIVE grants AS ( + SELECT + descendantUuid, + ascendantUuid + FROM + RbacGrants + WHERE + descendantUuid = objectId + UNION ALL + SELECT + "grant".descendantUuid, + "grant".ascendantUuid + FROM + RbacGrants "grant" + INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid + ) SELECT + ascendantUuid + FROM + grants +); +$$; + + +CREATE OR REPLACE FUNCTION findGrantees(grantedId uuid) + RETURNS SETOF RbacReference + RETURNS NULL ON NULL INPUT + LANGUAGE sql AS $$ + SELECT reference.* + FROM ( + WITH RECURSIVE grants AS ( + SELECT + descendantUuid, + ascendantUuid + FROM + RbacGrants + WHERE + descendantUuid = grantedId + UNION ALL + SELECT + "grant".descendantUuid, + "grant".ascendantUuid + FROM + RbacGrants "grant" + INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid + ) SELECT + ascendantUuid + FROM + grants + ) as grantee + JOIN RbacReference reference ON reference.uuid=grantee.ascendantUuid; +$$; + +CREATE OR REPLACE FUNCTION isGranted(granteeId uuid, grantedId uuid) + RETURNS bool + RETURNS NULL ON NULL INPUT + LANGUAGE sql AS $$ +SELECT granteeId=grantedId OR granteeId IN ( + WITH RECURSIVE grants AS ( + SELECT + descendantUuid, + ascendantUuid + FROM + RbacGrants + WHERE + descendantUuid = grantedId + UNION ALL + SELECT + "grant".descendantUuid, + "grant".ascendantUuid + FROM + RbacGrants "grant" + INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid + ) SELECT + ascendantUuid + FROM + grants +); +$$; + +CREATE OR REPLACE FUNCTION isPermissionGrantedToSubject(permissionId uuid, subjectId uuid) + RETURNS BOOL + STABLE LEAKPROOF + LANGUAGE sql AS $$ + SELECT EXISTS ( + SELECT * FROM RbacUser WHERE uuid IN ( + WITH RECURSIVE grants AS ( + SELECT + descendantUuid, + ascendantUuid + FROM + RbacGrants g + WHERE + g.descendantUuid = permissionId + UNION ALL + SELECT + g.descendantUuid, + g.ascendantUuid + FROM + RbacGrants g + INNER JOIN grants recur ON recur.ascendantUuid = g.descendantUuid + ) SELECT + ascendantUuid + FROM + grants + WHERE ascendantUuid=subjectId + ) + ); +$$; + +SET SESSION AUTHORIZATION DEFAULT; +CREATE ROLE admin; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin; +CREATE ROLE restricted; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; + + +-- ======================================================== +-- Current User +-- -------------------------------------------------------- + + +CREATE OR REPLACE FUNCTION currentUser() + RETURNS varchar(63) + STABLE LEAKPROOF + LANGUAGE plpgsql AS $$ +DECLARE + currentUser VARCHAR(63); +BEGIN + BEGIN + currentUser := current_setting('hsadminng.currentUser'); + EXCEPTION WHEN OTHERS THEN + currentUser := NULL; + END; + IF (currentUser IS NULL OR currentUser = '') THEN + RAISE EXCEPTION 'hsadminng.currentUser must be defined, please use "SET LOCAL ...;"'; + END IF; + RETURN currentUser; +END; $$; + +SET SESSION AUTHORIZATION DEFAULT; +CREATE OR REPLACE FUNCTION currentUserId() + RETURNS uuid + STABLE LEAKPROOF + LANGUAGE plpgsql AS $$ +DECLARE + currentUser VARCHAR(63); + currentUserId uuid; +BEGIN + currentUser := currentUser(); + currentUserId = (SELECT uuid FROM RbacUser WHERE name = currentUser); + RETURN currentUserId; +END; $$; + + +CREATE OR REPLACE FUNCTION assumedRoles() + RETURNS varchar(63)[] + STABLE LEAKPROOF + LANGUAGE plpgsql AS $$ +DECLARE + currentSubject VARCHAR(63); +BEGIN + BEGIN + currentSubject := current_setting('hsadminng.assumedRoles'); + EXCEPTION WHEN OTHERS THEN + RETURN NULL; + END; + IF (currentSubject = '') THEN + RETURN NULL; + END IF; + RETURN string_to_array(currentSubject, ';'); +END; $$; + + +-- ROLLBACK; +SET SESSION AUTHORIZATION DEFAULT; +CREATE OR REPLACE FUNCTION currentSubjectIds() + RETURNS uuid[] + STABLE LEAKPROOF + LANGUAGE plpgsql AS $$ +DECLARE + assumedRoles VARCHAR(63)[]; + currentUserId uuid; + assumedRoleIds uuid[]; + assumedRoleId uuid; +BEGIN + currentUserId := currentUserId(); + assumedRoles := assumedRoles(); + IF ( assumedRoles IS NULL ) THEN + RETURN currentUserId; + END IF; + + RAISE NOTICE 'assuming roles: %', assumedRoles; + + SELECT ARRAY_AGG(uuid) FROM RbacRole WHERE name = ANY(assumedRoles) INTO assumedRoleIds; + FOREACH assumedRoleId IN ARRAY assumedRoleIds LOOP + IF ( NOT isGranted(currentUserId, assumedRoleId) ) THEN + RAISE EXCEPTION 'user % has no permission to assume role %', currentUser(), assumedRoleId; + END IF; + END LOOP; + RETURN assumedRoleIds; +END; $$; diff --git a/sql/11-rbac-view.sql b/sql/11-rbac-view.sql new file mode 100644 index 00000000..0324f47e --- /dev/null +++ b/sql/11-rbac-view.sql @@ -0,0 +1,89 @@ + +-- ======================================================== +-- Options for SELECT under RBAC rules +-- -------------------------------------------------------- + +-- access control via view policy and isPermissionGrantedToSubject - way too slow (33 s 617ms for 1 million rows) +SET SESSION AUTHORIZATION DEFAULT; +CREATE ROLE admin; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin; +CREATE ROLE restricted; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; + +SET SESSION AUTHORIZATION DEFAULT; +ALTER TABLE customer DISABLE ROW LEVEL SECURITY; +ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +ALTER TABLE customer FORCE ROW LEVEL SECURITY; +DROP POLICY IF EXISTS customer_policy ON customer; +CREATE POLICY customer_policy ON customer + FOR SELECT + TO restricted + USING ( + -- id=1000 + isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserId()) + ); + +SET SESSION AUTHORIZATION restricted; +SET hsadminng.currentUser TO 'alex@example.com'; +SELECT * from customer; + +-- access control via view-rule and isPermissionGrantedToSubject - way too slow (35 s 580 ms for 1 million rows) +SET SESSION SESSION AUTHORIZATION DEFAULT; +DROP VIEW cust_view; +CREATE VIEW cust_view AS +SELECT * FROM customer; +CREATE OR REPLACE RULE "_RETURN" AS + ON SELECT TO cust_view + DO INSTEAD + SELECT * FROM customer WHERE isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserId()); +SELECT * from cust_view LIMIT 10; + +select queryAllPermissionsOfSubjectId(findRbacUser('mike@hostsharing.net')); + +-- access control via view-rule with join to recursive permissions - really fast (38ms for 1 million rows) +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS cust_view; +CREATE OR REPLACE VIEW cust_view AS +SELECT * +FROM customer; +CREATE OR REPLACE RULE "_RETURN" AS + ON SELECT TO cust_view + DO INSTEAD + SELECT c.uuid, c.reference, c.prefix FROM customer AS c + JOIN queryAllPermissionsOfSubjectId(currentUserId()) AS p + ON p.objectTable='customer' AND p.objectUuid=c.uuid AND p.op in ('*', 'view'); +GRANT ALL PRIVILEGES ON cust_view TO restricted; + +SET SESSION SESSION AUTHORIZATION restricted; +SET hsadminng.currentUser TO 'alex@example.com'; +SELECT * from cust_view; + + +-- access control via view with join to recursive permissions - really fast (38ms for 1 million rows) +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS cust_view; +CREATE OR REPLACE VIEW cust_view AS + SELECT c.uuid, c.reference, c.prefix + FROM customer AS c + JOIN queryAllPermissionsOfSubjectId(currentUserId()) AS p + ON p.objectUuid=c.uuid AND p.op in ('*', 'view'); +GRANT ALL PRIVILEGES ON cust_view TO restricted; + +SET SESSION SESSION AUTHORIZATION restricted; +-- SET hsadminng.currentUser TO 'alex@example.com'; +SET hsadminng.currentUser TO 'mike@hostsharing.net'; +-- SET hsadminng.currentUser TO 'aaaaouq@example.com'; +SELECT * from cust_view where reference=1144150; + +select rr.uuid, rr.type from RbacGrants g + join RbacReference RR on g.ascendantUuid = RR.uuid + where g.descendantUuid in ( + select uuid from queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com')) + where objectTable='customer' and op in ('*', 'view')); + +call grantRoleToUser(findRoleId('customer#aaa.admin'), findRbacUser('aaaaouq@example.com')); + +select queryAllPermissionsOfSubjectId(findRbacUser('aaaaouq@example.com')); + diff --git a/sql/19-rbac-tests.sql b/sql/19-rbac-tests.sql new file mode 100644 index 00000000..4cf65b9f --- /dev/null +++ b/sql/19-rbac-tests.sql @@ -0,0 +1,50 @@ +-- ======================================================== +-- Some Tests +-- -------------------------------------------------------- + + +select isGranted(findRoleId('administrators'), findRoleId('package#aaa00.owner')); +select isGranted(findRoleId('package#aaa00.owner'), findRoleId('administrators')); +-- call grantRoleToRole(findRoleId('package#aaa00.owner'), findRoleId('administrators')); +-- call grantRoleToRole(findRoleId('administrators'), findRoleId('package#aaa00.owner')); + +select count(*) +FROM queryAllPermissionsOfSubjectIdForObjectUuids(findRbacUser('sven@hostsharing.net'), + ARRAY(select uuid from customer where reference < 1100000)); +select count(*) +FROM queryAllPermissionsOfSubjectId(findRbacUser('sven@hostsharing.net')); +select * +FROM queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com')); +select * +FROM queryAllPermissionsOfSubjectId(findRbacUser('rosa@example.com')); + +select * +FROM queryAllRbacUsersWithPermissionsFor(findPermissionId('customer', + (SELECT uuid FROM RbacObject WHERE objectTable = 'customer' LIMIT 1), + 'add-package')); +select * +FROM queryAllRbacUsersWithPermissionsFor(findPermissionId('package', + (SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1), + 'delete')); + +DO LANGUAGE plpgsql +$$ + DECLARE + userId uuid; + result bool; + BEGIN + userId = findRbacUser('mike@hostsharing.net'); + result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'add-package'), userId)); + IF (result) THEN + RAISE EXCEPTION 'expected permission NOT to be granted, but it is'; + end if; + + result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'view'), userId)); + IF (NOT result) THEN + RAISE EXCEPTION 'expected permission to be granted, but it is NOT'; + end if; + + RAISE LOG 'isPermissionGrantedToSubjectId test passed'; + END; +$$; + diff --git a/sql/20-hs-base.sql b/sql/20-hs-base.sql new file mode 100644 index 00000000..ff745295 --- /dev/null +++ b/sql/20-hs-base.sql @@ -0,0 +1,18 @@ + +-- create administrators role with two assigned users +do language plpgsql $$ + declare + admins uuid ; + begin + admins = createRole('administrators'); + call grantRoleToUser(admins, createRbacUser('mike@hostsharing.net')); + call grantRoleToUser(admins, createRbacUser('sven@hostsharing.net')); + commit; + end; +$$; + + +BEGIN TRANSACTION; +SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; +select * from RbacUser where uuid=currentUserId(); +END TRANSACTION; diff --git a/sql/21-hs-customer.sql b/sql/21-hs-customer.sql new file mode 100644 index 00000000..5e050cd4 --- /dev/null +++ b/sql/21-hs-customer.sql @@ -0,0 +1,134 @@ + +-- ======================================================== +-- Customer example with RBAC +-- -------------------------------------------------------- + +SET SESSION SESSION AUTHORIZATION DEFAULT ; + +CREATE TABLE IF NOT EXISTS customer ( + uuid uuid UNIQUE REFERENCES RbacObject(uuid), + reference int not null unique CHECK (reference BETWEEN 10000 AND 99999), + prefix character(3) unique, + adminUserName varchar(63) +); + +DROP TRIGGER IF EXISTS createRbacObjectForCustomer_Trigger ON customer; +CREATE TRIGGER createRbacObjectForCustomer_Trigger + BEFORE INSERT ON customer + FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); + +CREATE OR REPLACE FUNCTION createRbacRulesForCustomer() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ +DECLARE + adminUserNameUuid uuid; + customerOwnerRoleId uuid; + customerAdminRoleId uuid; +BEGIN + IF TG_OP <> 'INSERT' THEN + RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; + END IF; + + -- an owner role is created and assigned to the administrators group + customerOwnerRoleId = createRole('customer#'||NEW.prefix||'.owner'); + call grantRoleToRole(customerOwnerRoleId, getRoleId('administrators', 'create')); + -- ... and permissions for all ops are assigned + call grantPermissionsToRole(customerOwnerRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + + -- ... also a customer admin role is created and granted to the customer owner role + customerAdminRoleId = createRole('customer#'||NEW.prefix||'.admin'); + call grantRoleToRole(customerAdminRoleId, customerOwnerRoleId); + -- ... to which a permission with view and add- ops is assigned + call grantPermissionsToRole(customerAdminRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view', 'add-package'])); + -- if a admin user is given for the customer, + IF (NEW.adminUserName IS NOT NULL) THEN + -- ... the customer admin role is also assigned to the admin user of the customer + adminUserNameUuid = findRoleId(NEW.adminUserName); + IF ( adminUserNameUuid IS NULL ) THEN + adminUserNameUuid = createRbacUser(NEW.adminUserName); + END IF; + call grantRoleToUser(customerAdminRoleId, adminUserNameUuid); + END IF; + + RETURN NEW; +END; $$; + +DROP TRIGGER IF EXISTS createRbacRulesForCustomer_Trigger ON customer; +CREATE TRIGGER createRbacRulesForCustomer_Trigger + AFTER INSERT ON customer + FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForCustomer(); + +CREATE OR REPLACE FUNCTION deleteRbacRulesForCustomer() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ +DECLARE + objectTable varchar = 'customer'; +BEGIN + IF TG_OP = 'DELETE' THEN + + -- delete the owner role (for admininstrators) + call deleteRole(findRoleId(objectTable||'#'||NEW.prefix||'.owner')); + + -- delete the customer admin role + call deleteRole(findRoleId(objectTable||'#'||NEW.prefix||'.admin')); + ELSE + RAISE EXCEPTION 'invalid usage of TRIGGER BEFORE DELETE'; + END IF; +END; $$; + +DROP TRIGGER IF EXISTS deleteRbacRulesForCustomer_Trigger ON customer; +CREATE TRIGGER deleteRbacRulesForCustomer_Trigger + BEFORE DELETE ON customer + FOR EACH ROW EXECUTE PROCEDURE deleteRbacRulesForCustomer(); + + +-- create RBAC restricted view + +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS cust_view; +DROP VIEW IF EXISTS customer_rv; +CREATE OR REPLACE VIEW customer_rv AS + SELECT DISTINCT target.* + FROM customer AS target + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + ON target.uuid = allowedObjId; +GRANT ALL PRIVILEGES ON customer_rv TO restricted; + + +-- generate Customer test data + +DO LANGUAGE plpgsql $$ + DECLARE + currentTask varchar; + custReference integer; + custRowId uuid; + custPrefix varchar; + custAdminName varchar; + BEGIN + SET hsadminng.currentUser TO ''; + + FOR t IN 0..9999 LOOP + currentTask = 'creating RBAC test customer #' || t; + SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net'; + SET LOCAL hsadminng.assumedRoles = ''; + SET LOCAL hsadminng.currentTask TO currentTask; + + -- When a new customer is created, + custReference = 10000 + t; + custRowId = uuid_generate_v4(); + custPrefix = intToVarChar(t, 3 ); + custAdminName = 'admin@' || custPrefix || '.example.com'; + + raise notice 'creating customer %:%', custReference, custPrefix; + insert into customer (reference, prefix, adminUserName) + VALUES (custReference, custPrefix, custAdminName); + + COMMIT; + + END LOOP; + + END; +$$; diff --git a/sql/22-hs-packages.sql b/sql/22-hs-packages.sql new file mode 100644 index 00000000..7322e3b1 --- /dev/null +++ b/sql/22-hs-packages.sql @@ -0,0 +1,117 @@ + +-- ======================================================== +-- Package example with RBAC +-- -------------------------------------------------------- + +SET SESSION SESSION AUTHORIZATION DEFAULT ; + +CREATE TABLE IF NOT EXISTS package ( + uuid uuid UNIQUE REFERENCES RbacObject(uuid), + name character varying(5), + customerUuid uuid REFERENCES customer(uuid) +); + +DROP TRIGGER IF EXISTS createRbacObjectForPackage_Trigger ON package; +CREATE TRIGGER createRbacObjectForPackage_Trigger + BEFORE INSERT ON package + FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); + +CREATE OR REPLACE FUNCTION createRbacRulesForPackage() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ +DECLARE + parentCustomer customer; + packageOwnerRoleId uuid; + packageTenantRoleId uuid; +BEGIN + IF TG_OP <> 'INSERT' THEN + RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; + END IF; + + SELECT * FROM customer AS c WHERE c.uuid=NEW.customerUuid INTO parentCustomer; + + -- an owner role is created and assigned to the customer's admin group + packageOwnerRoleId = createRole('package#'||NEW.name||'.owner'); + call grantRoleToRole(packageOwnerRoleId, getRoleId('customer#'||parentCustomer.prefix||'.admin', 'fail')); + + -- ... and permissions for all ops are assigned + call grantPermissionsToRole(packageOwnerRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + + -- ... also a package tenant role is created and assigned to the package owner as well + packageTenantRoleId = createRole('package#'||NEW.name||'.tenant'); + call grantRoleToRole(packageTenantRoleId, packageOwnerRoleId); + + -- ... to which a permission with view operation is assigned + call grantPermissionsToRole(packageTenantRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view'])); + + RETURN NEW; +END; $$; + +DROP TRIGGER IF EXISTS createRbacRulesForPackage_Trigger ON package; +CREATE TRIGGER createRbacRulesForPackage_Trigger + AFTER INSERT ON package + FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForPackage(); + +CREATE OR REPLACE FUNCTION deleteRbacRulesForPackage() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ +BEGIN + IF TG_OP = 'DELETE' THEN + -- TODO + ELSE + RAISE EXCEPTION 'invalid usage of TRIGGER BEFORE DELETE'; + END IF; +END; $$; + +DROP TRIGGER IF EXISTS deleteRbacRulesForPackage_Trigger ON customer; +CREATE TRIGGER deleteRbacRulesForPackage_Trigger + BEFORE DELETE ON customer + FOR EACH ROW EXECUTE PROCEDURE deleteRbacRulesForPackage(); + +-- create RBAC restricted view + +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE package ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS package_rv; +CREATE OR REPLACE VIEW package_rv AS + SELECT DISTINCT target.* + FROM package AS target + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + ON target.uuid = allowedObjId; +GRANT ALL PRIVILEGES ON package_rv TO restricted; + + + +-- generate Package test data + +DO LANGUAGE plpgsql $$ + DECLARE + cust customer; + pacName varchar; + currentTask varchar; + custAdmin varchar; + BEGIN + SET hsadminng.currentUser TO ''; + + FOR cust IN (SELECT * FROM customer) LOOP + FOR t IN 0..9 LOOP + pacName = cust.prefix || TO_CHAR(t, 'fm00'); + currentTask = 'creating RBAC test package #'|| pacName || ' for customer ' || cust.prefix || ' #' || cust.uuid; + RAISE NOTICE 'task: %', currentTask; + + custAdmin = 'admin@' || cust.prefix || '.example.com'; + SET LOCAL hsadminng.currentUser TO custAdmin; + SET LOCAL hsadminng.assumedRoles = ''; + SET LOCAL hsadminng.currentTask TO currentTask; + + insert into package (name, customerUuid) + VALUES (pacName, cust.uuid); + + COMMIT; + END LOOP; + END LOOP; + END; +$$; + diff --git a/sql/23-hs-unixuser.sql b/sql/23-hs-unixuser.sql new file mode 100644 index 00000000..77ed62b6 --- /dev/null +++ b/sql/23-hs-unixuser.sql @@ -0,0 +1,106 @@ + +-- ======================================================== +-- UnixUser example with RBAC +-- -------------------------------------------------------- + +SET SESSION SESSION AUTHORIZATION DEFAULT ; + +CREATE TABLE IF NOT EXISTS UnixUser ( + uuid uuid UNIQUE REFERENCES RbacObject(uuid), + name character varying(32), + packageUuid uuid REFERENCES package(uuid) +); + +DROP TRIGGER IF EXISTS createRbacObjectForUnixUser_Trigger ON UnixUser; +CREATE TRIGGER createRbacObjectForUnixUser_Trigger + BEFORE INSERT ON UnixUser + FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); + +CREATE OR REPLACE FUNCTION createRbacRulesForUnixUser() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ +DECLARE + parentPackage package; + unixuserOwnerRoleId uuid; + unixuserAdminRoleId uuid; + unixuserTenantRoleId uuid; +BEGIN + IF TG_OP <> 'INSERT' THEN + RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; + END IF; + + SELECT * FROM package WHERE uuid=NEW.packageUuid into parentPackage; + + -- an owner role is created and assigned to the package owner group + unixuserOwnerRoleId = createRole('unixuser#'||NEW.name||'.owner'); + call grantRoleToRole(unixuserOwnerRoleId, getRoleId('package#'||parentPackage.name||'.owner', 'fail')); + -- ... and permissions for all ops are assigned + call grantPermissionsToRole(unixuserOwnerRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + + -- ... also a unixuser admin role is created and assigned to the unixuser owner as well + unixuserAdminRoleId = createRole('unixuser#'||NEW.name||'.admin'); + call grantRoleToRole(unixuserAdminRoleId, unixuserOwnerRoleId); + -- ... to which a permission with view operation is assigned + call grantPermissionsToRole(unixuserAdminRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-domain'])); + + -- ... also a unixuser tenant role is created and assigned to the unixuser admin + unixuserTenantRoleId = createRole('unixuser#'||NEW.name||'.tenant'); + call grantRoleToRole(unixuserTenantRoleId, unixuserAdminRoleId); + -- ... to which a permission with view operation is assigned + call grantPermissionsToRole(unixuserTenantRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view'])); + + RETURN NEW; +END; $$; + +DROP TRIGGER IF EXISTS createRbacRulesForUnixUser_Trigger ON UnixUser; +CREATE TRIGGER createRbacRulesForUnixUser_Trigger + AFTER INSERT ON UnixUser + FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForUnixUser(); + +-- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForUnixUser() + + +-- create RBAC restricted view + +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS unixuser_rv; +CREATE OR REPLACE VIEW unixuser_rv AS + SELECT DISTINCT target.* + FROM unixuser AS target + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + ON target.uuid = allowedObjId; +GRANT ALL PRIVILEGES ON unixuser_rv TO restricted; + + +-- generate UnixUser test data + +DO LANGUAGE plpgsql $$ + DECLARE + pac package; + pacAdmin varchar; + currentTask varchar; + BEGIN + SET hsadminng.currentUser TO ''; + + FOR pac IN (SELECT * FROM package) LOOP + FOR t IN 0..9 LOOP + currentTask = 'creating RBAC test unixuser #' || t || ' for package ' || pac.name|| ' #' || pac.uuid; + RAISE NOTICE 'task: %', currentTask; + pacAdmin = 'admin@' || pac.name || '.example.com'; + SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net'; -- TODO: use a package-admin + SET LOCAL hsadminng.assumedRoles = ''; + SET LOCAL hsadminng.currentTask TO currentTask; + + INSERT INTO unixuser (name, packageUuid) + VALUES (pac.name||'-'|| intToVarChar(t, 4), pac.uuid); + + COMMIT; + END LOOP; + END LOOP; + + END; +$$; diff --git a/sql/24-hs-domain.sql b/sql/24-hs-domain.sql new file mode 100644 index 00000000..f19e1f97 --- /dev/null +++ b/sql/24-hs-domain.sql @@ -0,0 +1,98 @@ + +-- ======================================================== +-- Domain example with RBAC +-- -------------------------------------------------------- + +SET SESSION SESSION AUTHORIZATION DEFAULT ; + +CREATE TABLE IF NOT EXISTS Domain ( + uuid uuid UNIQUE REFERENCES RbacObject(uuid), + name character varying(32), + unixUserUuid uuid REFERENCES unixuser(uuid) +); + +DROP TRIGGER IF EXISTS createRbacObjectForDomain_Trigger ON Domain; +CREATE TRIGGER createRbacObjectForDomain_Trigger + BEFORE INSERT ON Domain + FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); + +CREATE OR REPLACE FUNCTION createRbacRulesForDomain() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ +DECLARE + parentUser unixuser; + domainOwnerRoleId uuid; +BEGIN + IF TG_OP <> 'INSERT' THEN + RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; + END IF; + + SELECT * FROM unixuser WHERE uuid=NEW.unixUserUuid into parentUser; + + -- an owner role is created and assigned to the unix user admin + RAISE NOTICE 'creating domain owner role: %', 'domain#'||NEW.name||'.owner'; + domainOwnerRoleId = getRoleId('domain#'||NEW.name||'.owner', 'create'); + call grantRoleToRole(domainOwnerRoleId, getRoleId('unixuser#'||parentUser.name||'.admin', 'fail')); + -- ... and permissions for all ops are assigned + call grantPermissionsToRole(domainOwnerRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + + RETURN NEW; +END; $$; + +DROP TRIGGER IF EXISTS createRbacRulesForDomain_Trigger ON Domain; +CREATE TRIGGER createRbacRulesForDomain_Trigger + AFTER INSERT ON Domain + FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForDomain(); + +-- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForDomain() + + +-- create RBAC restricted view + +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS domain_rv; +CREATE OR REPLACE VIEW domain_rv AS + SELECT DISTINCT target.* + FROM Domain AS target + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + ON target.uuid = allowedObjId; +GRANT ALL PRIVILEGES ON domain_rv TO restricted; + + +-- generate Domain test data + +DO LANGUAGE plpgsql $$ + DECLARE + uu unixuser; + pac package; + pacAdmin varchar; + currentTask varchar; + BEGIN + SET hsadminng.currentUser TO ''; + + FOR uu IN (SELECT * FROM unixuser) LOOP + IF ( random() < 0.3 ) THEN + FOR t IN 0..2 LOOP + currentTask = 'creating RBAC test Domain #' || t || ' for UnixUser ' || uu.name|| ' #' || uu.uuid; + RAISE NOTICE 'task: %', currentTask; + + SELECT * FROM package WHERE uuid=uu.packageUuid INTO pac; + pacAdmin = 'admin@' || pac.name || '.example.com'; + SET LOCAL hsadminng.currentUser TO pacAdmin; + SET LOCAL hsadminng.assumedRoles = ''; + SET LOCAL hsadminng.currentTask TO currentTask; + + INSERT INTO Domain (name, unixUserUuid) + VALUES ('dom-' || t || '.' || pac.name || '.example.org' , uu.uuid); + + COMMIT; + END LOOP; + END IF; + END LOOP; + + END; +$$; + + diff --git a/sql/25-hs-emailaddress.sql b/sql/25-hs-emailaddress.sql new file mode 100644 index 00000000..6956fded --- /dev/null +++ b/sql/25-hs-emailaddress.sql @@ -0,0 +1,122 @@ + +-- ======================================================== +-- EMailAddress example with RBAC +-- -------------------------------------------------------- + +SET SESSION SESSION AUTHORIZATION DEFAULT ; + +CREATE TABLE IF NOT EXISTS EMailAddress ( + uuid uuid UNIQUE REFERENCES RbacObject(uuid), + localPart character varying(64), + domainUuid uuid REFERENCES domain(uuid) +); + +DROP TRIGGER IF EXISTS createRbacObjectForEMailAddress_Trigger ON EMailAddress; +CREATE TRIGGER createRbacObjectForEMailAddress_Trigger + BEFORE INSERT ON EMailAddress + FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); + +CREATE OR REPLACE FUNCTION createRbacRulesForEMailAddress() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ +DECLARE + eMailAddress varchar; + parentDomain domain; + eMailAddressOwnerRoleId uuid; + eMailAddressTenantRoleId uuid; +BEGIN + IF TG_OP <> 'INSERT' THEN + RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; + END IF; + + SELECT * FROM domain WHERE uuid=NEW.domainUuid into parentDomain; + eMailAddress = NEW.localPart || '@' || parentDomain.name; + + -- an owner role is created and assigned to the domain owner + eMailAddressOwnerRoleId = getRoleId('emailaddress#'||eMailAddress||'.owner', 'create'); + call grantRoleToRole(eMailAddressOwnerRoleId, getRoleId('domain#'||parentDomain.name||'.owner', 'fail')); + -- ... and permissions for all ops are assigned + call grantPermissionsToRole(eMailAddressOwnerRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + + -- a tenant role is created and assigned to a user with the new email address + eMailAddressTenantRoleId = getRoleId('emailaddress#'||eMailAddress||'.tenant', 'create'); + call grantRoleToUser(eMailAddressTenantRoleId, getRbacUserId(eMailAddress, 'create')); + -- ... and permissions for all ops are assigned + call grantPermissionsToRole(eMailAddressTenantRoleId, + createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); -- TODO '*' -> 'edit', 'view' + + RETURN NEW; +END; $$; + +DROP TRIGGER IF EXISTS createRbacRulesForEMailAddress_Trigger ON EMailAddress; +CREATE TRIGGER createRbacRulesForEMailAddress_Trigger + AFTER INSERT ON EMailAddress + FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForEMailAddress(); + +-- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForEMailAddress() + + +-- create RBAC restricted view + +abort; +set session authorization default ; +START TRANSACTION; + SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; + SET LOCAL hsadminng.assumedRoles = 'customer#bbb.owner;customer#bbc.owner'; + -- SET LOCAL hsadminng.assumedRoles = 'package#bbb00.owner;package#bbb01.owner'; + + select count(*) from queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds(), 7) as a + join rbacobject as o on a=o.uuid; + + /* SELECT DISTINCT target.* + FROM EMailAddress AS target + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + ON target.uuid = allowedObjId;*/ +END TRANSACTION; + +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS EMailAddress_rv; +CREATE OR REPLACE VIEW EMailAddress_rv AS + SELECT DISTINCT target.* + FROM EMailAddress AS target + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + ON target.uuid = allowedObjId; +GRANT ALL PRIVILEGES ON EMailAddress_rv TO restricted; + + +-- generate EMailAddress test data + +DO LANGUAGE plpgsql $$ + DECLARE + pac package; + uu unixuser; + dom domain; + pacAdmin varchar; + currentTask varchar; + BEGIN + SET hsadminng.currentUser TO ''; + + FOR dom IN (SELECT * FROM domain) LOOP + FOR t IN 0..5 LOOP + currentTask = 'creating RBAC test EMailAddress #' || t || ' for Domain ' || dom.name; + RAISE NOTICE 'task: %', currentTask; + + SELECT * FROM unixuser WHERE uuid=dom.unixuserUuid INTO uu; + SELECT * FROM package WHERE uuid=uu.packageUuid INTO pac; + pacAdmin = 'admin@' || pac.name || '.example.com'; + SET LOCAL hsadminng.currentUser TO pacAdmin; + SET LOCAL hsadminng.assumedRoles = ''; + SET LOCAL hsadminng.currentTask TO currentTask; + + INSERT INTO EMailAddress (localPart, domainUuid) + VALUES ('local' || t, dom.uuid); + + COMMIT; + END LOOP; + END LOOP; + END; +$$; + + diff --git a/sql/29-hs-statistics.sql b/sql/29-hs-statistics.sql new file mode 100644 index 00000000..7dbb3f83 --- /dev/null +++ b/sql/29-hs-statistics.sql @@ -0,0 +1,24 @@ + +-- ======================================================== +-- Some Business Table Statistics +-- -------------------------------------------------------- + +DROP VIEW IF EXISTS "BusinessTableStatisticsV"; +CREATE VIEW "BusinessTableStatisticsV" AS +SELECT no, to_char("count", '999 999 999') as "count", to_char("required", '999 999 999') as "required", to_char("count"::float/"required"::float, '990.9') as "factor", "table" +FROM (select 1 as no, count(*) as "count", 7000 as "required", 'customers' as "table" + from customer + UNION + select 2 as no, count(*) as "count", 15000 as "required", 'packages' as "table" + from package + UNION + select 3 as no, count(*) as "count", 150000 as "required", 'unixuser' as "table" + from unixuser + UNION + select 4 as no, count(*) as "count", 100000 as "required", 'domain' as "table" + from domain + UNION + select 5 as no, count(*) as "count", 500000 as "required", 'emailaddress' as "table" + from emailaddress + ) totals +ORDER BY totals.no; diff --git a/sql/examples.sql b/sql/examples.sql new file mode 100644 index 00000000..bcbfe2fc --- /dev/null +++ b/sql/examples.sql @@ -0,0 +1,62 @@ +-- ======================================================== +-- First Example Entity with History +-- -------------------------------------------------------- + +CREATE TABLE IF NOT EXISTS customer ( + "id" SERIAL PRIMARY KEY, + "reference" int not null unique, -- 10000-99999 + "prefix" character(3) unique + ); + +CALL create_historicization('customer'); + + +-- ======================================================== +-- Second Example Entity with History +-- -------------------------------------------------------- + +CREATE TABLE IF NOT EXISTS package_type ( + "id" serial PRIMARY KEY, + "name" character varying(8) + ); + +CALL create_historicization('package_type'); + +-- ======================================================== +-- Third Example Entity with History +-- -------------------------------------------------------- + +CREATE TABLE IF NOT EXISTS package ( + "id" serial PRIMARY KEY, + "name" character varying(5), + "customer_id" INTEGER REFERENCES customer(id) + ); + +CALL create_historicization('package'); + + +-- ======================================================== +-- query historical data +-- -------------------------------------------------------- + + +ABORT; +BEGIN TRANSACTION; +SET LOCAL hsadminng.currentUser TO 'mih42_customer_aaa'; +SET LOCAL hsadminng.currentTask TO 'adding customer_aaa'; +INSERT INTO package (customer_id, name) VALUES (10000, 'aaa00'); +COMMIT; + +SET SESSION SESSION AUTHORIZATION DEFAULT; +CREATE ROLE hs_sel_package_1000000; +GRANT hs_sel_package_1000000 to hs_sel_customer_10000; + +SET SESSION SESSION AUTHORIZATION mih42_customer_aaa; +SELECT pg_has_role('hs_sel_package_1000000', 'MEMBER'); + + +-- Usage: + +SET hsadminng.timestamp TO '2022-07-12 08:53:27.723315'; +SET hsadminng.timestamp TO '2022-07-12 11:38:27.723315'; +SELECT * FROM customer_hv p WHERE prefix = 'aaa'; diff --git a/sql/historization.sql b/sql/historization.sql new file mode 100644 index 00000000..3c038522 --- /dev/null +++ b/sql/historization.sql @@ -0,0 +1,166 @@ + +-- ======================================================== +-- Historization +-- -------------------------------------------------------- + +CREATE TABLE "tx_history" ( + "tx_id" BIGINT NOT NULL UNIQUE, + "tx_timestamp" TIMESTAMP NOT NULL, + "user" VARCHAR(64) NOT NULL, -- references postgres user + "task" VARCHAR NOT NULL +); + +CREATE TYPE "operation" AS ENUM ('INSERT', 'UPDATE', 'DELETE', 'TRUNCATE'); + +-- see https://www.postgresql.org/docs/current/plpgsql-trigger.html + +CREATE OR REPLACE FUNCTION historicize() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ +DECLARE +currentUser VARCHAR(64); + currentTask varchar; + "row" RECORD; + "alive" BOOLEAN; + "sql" varchar; +BEGIN + -- determine user_id +BEGIN + currentUser := current_setting('hsadminng.currentUser'); +EXCEPTION WHEN OTHERS THEN + currentUser := NULL; +END; + IF (currentUser IS NULL OR currentUser = '') THEN + RAISE EXCEPTION 'hsadminng.currentUser must be defined, please use "SET LOCAL ...;"'; +END IF; + RAISE NOTICE 'currentUser: %', currentUser; + + -- determine task + currentTask = current_setting('hsadminng.currentTask'); + IF (currentTask IS NULL OR length(currentTask) < 12) THEN + RAISE EXCEPTION 'hsadminng.currentTask (%) must be defined and min 12 characters long, please use "SET LOCAL ...;"', currentTask; +END IF; + RAISE NOTICE 'currentTask: %', currentTask; + + IF (TG_OP = 'INSERT') OR (TG_OP = 'UPDATE') THEN + "row" := NEW; + "alive" := TRUE; +ELSE -- DELETE or TRUNCATE + "row" := OLD; + "alive" := FALSE; +END IF; + +sql := format('INSERT INTO tx_history VALUES (txid_current(), now(), %1L, %2L) ON CONFLICT DO NOTHING', currentUser, currentTask); + RAISE NOTICE 'sql: %', sql; +EXECUTE sql; +sql := format('INSERT INTO %3$I_versions VALUES (DEFAULT, txid_current(), %1$L, %2$L, $1.*)', TG_OP, alive, TG_TABLE_NAME); + RAISE NOTICE 'sql: %', sql; +EXECUTE sql USING "row"; + +RETURN "row"; +END; $$; + +CREATE OR REPLACE PROCEDURE create_historical_view(baseTable varchar) + LANGUAGE plpgsql AS $$ +DECLARE +createTriggerSQL varchar; + viewName varchar; + versionsTable varchar; + createViewSQL varchar; + baseCols varchar; +BEGIN + + viewName = quote_ident(format('%s_hv', baseTable)); + versionsTable = quote_ident(format('%s_versions', baseTable)); + baseCols = (SELECT string_agg(quote_ident(column_name), ', ') + FROM information_schema.columns + WHERE table_schema = 'public' AND table_name = baseTable); + + createViewSQL = format( + 'CREATE OR REPLACE VIEW %1$s AS' || + '(' || + ' SELECT %2$s' || + ' FROM %3$s' || + ' WHERE alive = TRUE' || + ' AND version_id IN' || + ' (' || + ' SELECT max(vt.version_id) AS history_id' || + ' FROM %3$s AS vt' || + ' JOIN tx_history as txh ON vt.tx_id = txh.tx_id' || + ' WHERE txh.tx_timestamp <= current_setting(''hsadminng.timestamp'')::timestamp' || + ' GROUP BY id' || + ' )' || + ')', + viewName, baseCols, versionsTable + ); + RAISE NOTICE 'sql: %', createViewSQL; +EXECUTE createViewSQL; + +createTriggerSQL = 'CREATE TRIGGER ' || baseTable || '_historicize' || + ' AFTER INSERT OR DELETE OR UPDATE ON ' || baseTable || + ' FOR EACH ROW EXECUTE PROCEDURE historicize()'; + RAISE NOTICE 'sql: %', createTriggerSQL; +EXECUTE createTriggerSQL; + +END; $$; + +CREATE OR REPLACE PROCEDURE create_historicization(baseTable varchar) + LANGUAGE plpgsql AS $$ +DECLARE +createHistTableSql varchar; + createTriggerSQL varchar; + viewName varchar; + versionsTable varchar; + createViewSQL varchar; + baseCols varchar; +BEGIN + + -- create the history table + createHistTableSql = '' || + 'CREATE TABLE ' || baseTable || '_versions (' || + ' version_id serial PRIMARY KEY,' || + ' tx_id bigint NOT NULL REFERENCES tx_history(tx_id),' || + ' trigger_op operation NOT NULL,' || + ' alive boolean not null,' || + + ' LIKE ' || baseTable || + ' EXCLUDING CONSTRAINTS' || + ' EXCLUDING STATISTICS' || + ')'; + RAISE NOTICE 'sql: %', createHistTableSql; + EXECUTE createHistTableSql; + + -- create the historical view + viewName = quote_ident(format('%s_hv', baseTable)); + versionsTable = quote_ident(format('%s_versions', baseTable)); + baseCols = (SELECT string_agg(quote_ident(column_name), ', ') + FROM information_schema.columns + WHERE table_schema = 'public' AND table_name = baseTable); + + createViewSQL = format( + 'CREATE OR REPLACE VIEW %1$s AS' || + '(' || + ' SELECT %2$s' || + ' FROM %3$s' || + ' WHERE alive = TRUE' || + ' AND version_id IN' || + ' (' || + ' SELECT max(vt.version_id) AS history_id' || + ' FROM %3$s AS vt' || + ' JOIN tx_history as txh ON vt.tx_id = txh.tx_id' || + ' WHERE txh.tx_timestamp <= current_setting(''hsadminng.timestamp'')::timestamp' || + ' GROUP BY id' || + ' )' || + ')', + viewName, baseCols, versionsTable + ); + RAISE NOTICE 'sql: %', createViewSQL; + EXECUTE createViewSQL; + + createTriggerSQL = 'CREATE TRIGGER ' || baseTable || '_historicize' || + ' AFTER INSERT OR DELETE OR UPDATE ON ' || baseTable || + ' FOR EACH ROW EXECUTE PROCEDURE historicize()'; + RAISE NOTICE 'sql: %', createTriggerSQL; + EXECUTE createTriggerSQL; + +END; $$; diff --git a/sql/history-demo.sql b/sql/history-demo.sql deleted file mode 100644 index df6493a7..00000000 --- a/sql/history-demo.sql +++ /dev/null @@ -1,105 +0,0 @@ --- --- Historization --- - -CREATE TABLE history ( - history_id serial PRIMARY KEY, - history_transaction bigint NOT NULL UNIQUE, - history_timestamp timestamp NOT NULL -); - -CREATE FUNCTION historicize() RETURNS trigger -AS $$ -BEGIN - IF (TG_OP = 'INSERT') OR (TG_OP = 'UPDATE') THEN - EXECUTE 'INSERT INTO history VALUES (DEFAULT, txid_current(), now()) ON CONFLICT DO NOTHING'; - EXECUTE format('INSERT INTO %I_history VALUES (DEFAULT, txid_current(), False, $1.*)', TG_TABLE_NAME) USING NEW; - RETURN NEW; - ELSE - EXECUTE 'INSERT INTO history VALUES (DEFAULT, txid_current(), now()) ON CONFLICT DO NOTHING'; - EXECUTE format('INSERT INTO %I_history VALUES (DEFAULT, txid_current(), True, $1.*)', TG_TABLE_NAME) USING OLD; - RETURN OLD; - END IF; -END; -$$ -LANGUAGE plpgsql; - --- --- Entity with History --- - -CREATE TABLE person ( - id serial PRIMARY KEY, - name character varying(50) NOT NULL UNIQUE, - email character varying(50) NOT NULL UNIQUE -); - -CREATE TABLE person_history ( - history_id serial PRIMARY KEY, - history_transaction bigint NOT NULL REFERENCES history(history_transaction), - history_tombstone boolean NOT NULL, - id integer NOT NULL, - name character varying(50) NOT NULL, - email character varying(50) NOT NULL -); - -CREATE TRIGGER person_historicize AFTER INSERT OR DELETE OR UPDATE ON person FOR EACH ROW EXECUTE PROCEDURE historicize(); - --- --- Sample data --- - -INSERT INTO person (name, email) VALUES ('michael', 'michael@hierweck.de'); -INSERT INTO person (name, email) VALUES ('annika', 'annika@hierweck.de'); - -UPDATE person SET email='mh@hierweck.de' WHERE name='michael'; -UPDATE person SET email='ah@hierweck.de' WHERE name='annika'; - -DELETE FROM person WHERE name='michael'; -DELETE FROM person WHERE name='annika'; - -INSERT INTO person (name, email) VALUES ('michael', 'michael@hierweck.de'); -INSERT INTO person (name, email) VALUES ('annika', 'annika@hierweck.de'); - -BEGIN; -INSERT INTO person (name, email) VALUES ('mx', 'mx@hierweck.de'); -INSERT INTO person (name, email) VALUES ('ax', 'ax@hierweck.de'); -UPDATE person SET email='mxx@hierweck.de' WHERE name='mx'; -UPDATE person SET email='axx@hierweck.de' WHERE name='ax'; -COMMIT; - --- --- Approach 1: Function --- --- --- Usage: --- --- SELECT * FROM person_history(12345, 'name'); --- - -CREATE OR REPLACE FUNCTION person_history(transaction bigint, VARIADIC groupby text[]) RETURNS TABLE ( - history_id integer, - history_transaction bigint, - history_tombstone boolean, - id integer, - name character varying(50), - email character varying(50) -) -AS $$ -BEGIN - RETURN QUERY EXECUTE format('SELECT * FROM person_history WHERE history_id IN (SELECT max(history_id) AS history_id FROM person_history WHERE history_transaction <= $1 GROUP BY %s)', array_to_string(groupby, ', ')) USING transaction; -END; -$$ -LANGUAGE plpgsql; - --- --- Approach 2: View --- --- Usage: --- --- SET history_transaction = 12345; --- SELECT * FROM person_history_view; --- - -CREATE VIEW person_history_view -AS (SELECT * FROM person_history WHERE history_id IN (SELECT max(history_id) AS history_id FROM person_history WHERE history_transaction <= current_setting('history.transaction')::bigint GROUP BY name)); diff --git a/sql/rbac-statistics.sql b/sql/rbac-statistics.sql new file mode 100644 index 00000000..3430bcb4 --- /dev/null +++ b/sql/rbac-statistics.sql @@ -0,0 +1,18 @@ + +DROP VIEW "RbacStatisticsV"; +CREATE VIEW "RbacStatisticsV" AS + SELECT no, to_char("count", '9 999 999 999') as "count", "table" + FROM ( + select 1 as no, count(*) as "count", 'login users' as "table" from RbacUser + UNION + select 2 as no, count(*) as "count", 'roles' as "table" from RbacRole + UNION + select 3 as no, count(*) as "count", 'permissions' as "table" from RbacPermission + UNION + select 4 as no, count(*) as "count", 'references' as "table" from RbacReference + UNION + select 5 as no, count(*) as "count", 'grants' as "table" from RbacGrants + UNION + select 6 as no, count(*) as "count", 'objects' as "table" from RbacObject + ) as totals + ORDER BY totals.no; diff --git a/sql/rbac.md b/sql/rbac.md new file mode 100644 index 00000000..4e4c1c5e --- /dev/null +++ b/sql/rbac.md @@ -0,0 +1,301 @@ +## *hsadmin-ng*'s Role-Based-Access-Management (RBAC) + +The requirements of *hsadmin-ng* include table-m row- and column-level-security for read and write access to business-objects. +More precisely, any access has to be controlled according to given rules depending on the accessing users, their roles and the accessed business-object. +Further, roles and business-objects are hierarchical. + +To avoid misunderstandings, we are using the term "business-object" what's usually called a "domain-object". +But as we are in the context of a webhosting infrastructure provider, "domain" would have a double meaning. + +Our implementation is based on Role-Based-Access-Management (RBAC) in conjunction with views and triggers on the business-objects. +As far as possible, we are using the same terms as defined in the RBAC standard, for our function names though, we chose more expressive names. + +In RBAC, subjects can be assigned to roles, roles can be hierarchical and eventually have assigned permissions. +A permission allows a specific operation (e.g. view or edit) on a specific (business-) object. + +You can find the entity structure as a UML class diagram as follows: + +```plantuml +@startuml +' left to right direction +top to bottom direction + +' hide the ugly E in a circle left to the entity name +hide circle + +' use right-angled line routing +skinparam linetype ortho + +package RBAC { + + ' forward declarations + entity RbacUser + entity RbacObject + + together { + + entity RbacRole + entity RbacPermission + enum RbacOperation + + RbacUser -[hidden]> RbacRole + RbacRole -[hidden]> RbacUser + } + + together { + entity RbacGrant + enum RbacReferenceType + entity RbacReference + } + RbacReference -[hidden]> RbacReferenceType + + entity RbacGrant { + ascendantUuid: uuid(RbackReference) + descendantUuid: uuid(RbackReference) + auto + } + RbacGrant o-> RbacReference + RbacGrant o-> RbacReference + + enum RbacReferenceType { + RbacUser + RbacRole + RbacPermission + } + RbacReferenceType ..> RbacUser + RbacReferenceType ..> RbacRole + RbacReferenceType ..> RbacPermission + + entity RbacReference { + *uuid : uuid <> + -- + type : RbacReferenceType + } + RbacReference o--> RbacReferenceType + entity RbacUser { + *uuid : uuid <> + -- + name : varchar + } + RbacUser o-- RbacReference + + entity RbacRole { + *uuid : uuid(RbacReference) + -- + name : varchar + } + RbacRole o-- RbacReference + + entity RbacPermission { + *uuid : uuid(RbacReference) + -- + objectUuid: RbacObject + op: RbacOperation + } + RbacPermission o-- RbacReference + RbacPermission o-- RbacOperation + RbacPermission *-- RbacObject + + enum RbacOperation { + add-package + add-domain + add-unixuser + ... + view + edit + delete + } + + entity RbacObject { + *uuid : uuid <> + -- + objectTable: varchar + } + RbacObject o- "Business Objects" +} + +package "Business Objects" { + + entity package + package *--u- RbacObject + + entity customer + customer *--u- RbacObject + + entity "..." as moreBusinessObjects + moreBusinessObjects *-u- RbacObject +} + +@enduml +``` + +### The RBAC Entity Types + +#### RbacReference + +An *RbacReference* is a generalization of all entity types which participate in the hierarchical role system, defined via *RbacGrant*. + +The primary key of the *RbacReference* and its referred object is always identical. + +#### RbacUser + +An *RbacUser* is a type of RBAC-subject which references a login account outside this system, identified by a name (usually an email-address). + +*RbacUser*s can be assigned to multiple *RbacRole*s, through which they can get permissions to *RbacObject*s. + +The primary key of the *RbacUser* is identical to its related *RbacReference*. + +#### RbacRole + +An *RbacRole* represents a collection of directly or indirectly assigned *RbacPermission*s. +Each *RbacRole* can be assigned to *RbacUser*s or to another *RbacRole*. + +Both kinds of assignments are represented via *RbacGrant*. + +*RbacRole* entities can *RbacObject*s, or more precise + +#### RbacPermission + +An *RbacPermission* allows a specific *RbacOperation* on a specific *RbacObject*. + +#### RbacOperation + +An *RbacOperation* determines, what an *RbacPermission* allows to do. +It can be one of: + +- **add-...** - permits creating new instances of specific entity types underneath the object specified by the permission, e.g. "add-package" +- **view** - permits reading the contents of the object specified by the permission +- **edit** - change the contents of the object specified by the permission +- **delete** - delete the object specified by the permission + +This list is extensible according to the needs of the access rule system. + +Please notice, that there is no **create-...** operation to create new instances of related business-object-types. +For such a singleton business-object-type, e.g. *Organization" or "Hostsharing" has to be defined, and its single entity is referred in the permission. +By this, the foreign key in *RbacPermission* can be defined as `NOT NULL`. + +#### RbacGrant + +The *RbacGrant* entities represent the access-rights structure from *RbacUser*s via hierarchical *RbacRoles* down to *RbacPermission*s. + +The core SQL queries to determine access rights are all recursive queries on the *RbacGrant* table. + +### Role naming + +Automatically generated roles are named as such: + +#### business-table#business-object-name.tenant +This role is assigned to users who manage objects underneath the object which is accessible through the role. +This rule usually gets only view permissions assigned. + +**Example** + +'dd' + +## Example Users, Roles, Permissions and Business-Objects + +```plantuml +@startuml +' left to right direction +top to bottom direction + +' hide the ugly E in a circle left to the entity name +hide circle + +' use right-angled line routing +' skinparam linetype ortho + +package RbacUsers { + object UserMike + object UserSuse + object UserPaul +} + +package RbacRoles { + object RoleAdministrators + object RoleCustXyz_Owner + object RoleCustXyz_Admin + object RolePackXyz00_Owner +} +RbacUsers -[hidden]> RbacRoles + +package RbacPermissions { + object PermCustXyz_View + object PermCustXyz_Edit + object PermCustXyz_Delete + object PermCustXyz_AddPackage + object PermPackXyz00_View + object PermPackXyz00_Edit + object PermPackXyz00_Delete + object PermPackXyz00_AddUser +} +RbacRoles -[hidden]> RbacPermissions + +package BusinessObjects { + object CustXyz + object PackXyz00 +} +RbacPermissions -[hidden]> BusinessObjects + +UserMike o---> RoleAdministrators +UserSuse o--> RoleCustXyz_Admin +UserPaul o--> RolePackXyz00_Owner + +RoleAdministrators o..> RoleCustXyz_Owner +RoleCustXyz_Owner o-> RoleCustXyz_Admin +RoleCustXyz_Admin o-> RolePackXyz00_Owner + +RoleCustXyz_Owner o--> PermCustXyz_Edit +RoleCustXyz_Owner o--> PermCustXyz_Delete +RoleCustXyz_Admin o--> PermCustXyz_View +RoleCustXyz_Admin o--> PermCustXyz_AddPackage +RolePackXyz00_Owner o--> PermPackXyz00_View +RolePackXyz00_Owner o--> PermPackXyz00_Edit +RolePackXyz00_Owner o--> PermPackXyz00_Delete +RolePackXyz00_Owner o--> PermPackXyz00_AddUser + +PermCustXyz_View o--> CustXyz +PermCustXyz_Edit o--> CustXyz +PermCustXyz_Delete o--> CustXyz +PermCustXyz_AddPackage o--> CustXyz +PermPackXyz00_View o--> PackXyz00 +PermPackXyz00_Edit o--> PackXyz00 +PermPackXyz00_Delete o--> PackXyz00 +PermPackXyz00_AddUser o--> PackXyz00 + +@enduml +``` + + +```plantuml +@startuml +left to right direction +' top to bottom direction + +' hide the ugly E in a circle left to the entity name +hide circle + +' use right-angled line routing +' skinparam linetype ortho + +package rbacPerms { + cust +} + +package rbacRoles { + entity administrators + entity custXXX +} + +package rbacUsers { + entity adminMike + adminMike <-- administrators + + entity adminSven + entity custXXX + entity pacAdmXXX00 +} + +@enduml +``` + diff --git a/src/test/features/gitkeep b/sql/rbac.pdf similarity index 100% rename from src/test/features/gitkeep rename to sql/rbac.pdf diff --git a/sql/rbac.sql b/sql/rbac.sql new file mode 100644 index 00000000..139597f9 --- /dev/null +++ b/sql/rbac.sql @@ -0,0 +1,2 @@ + + diff --git a/src/main/docker/.dockerignore b/src/main/docker/.dockerignore deleted file mode 100644 index b03bdc71..00000000 --- a/src/main/docker/.dockerignore +++ /dev/null @@ -1,14 +0,0 @@ -# https://docs.docker.com/engine/reference/builder/#dockerignore-file -classes/ -generated-sources/ -generated-test-sources/ -h2db/ -maven-archiver/ -maven-status/ -reports/ -surefire-reports/ -test-classes/ -test-results/ -www/ -!*.jar -!*.war diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile deleted file mode 100644 index 43fadcb6..00000000 --- a/src/main/docker/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM openjdk:8-jre-alpine - -ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \ - JHIPSTER_SLEEP=0 \ - JAVA_OPTS="" - -# Add a jhipster user to run our application so that it doesn't need to run as root -RUN adduser -D -s /bin/sh jhipster -WORKDIR /home/jhipster - -ADD entrypoint.sh entrypoint.sh -RUN chmod 755 entrypoint.sh && chown jhipster:jhipster entrypoint.sh -USER jhipster - -ENTRYPOINT ["./entrypoint.sh"] - -EXPOSE 8080 - -ADD *.war app.war - diff --git a/src/main/docker/app.yml b/src/main/docker/app.yml deleted file mode 100644 index 09f053d1..00000000 --- a/src/main/docker/app.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '2' -services: - hsadminng-app: - image: hsadminng - environment: - - _JAVA_OPTIONS=-Xmx512m -Xms256m - - SPRING_PROFILES_ACTIVE=prod,swagger - - SPRING_DATASOURCE_URL=jdbc:postgresql://hsadminng-postgresql:5432/hsadminNg - - JHIPSTER_SLEEP=10 # gives time for the database to boot before the application - ports: - - 8080:8080 - hsadminng-postgresql: - extends: - file: postgresql.yml - service: hsadminng-postgresql diff --git a/src/main/docker/entrypoint.sh b/src/main/docker/entrypoint.sh deleted file mode 100644 index ccffafb5..00000000 --- a/src/main/docker/entrypoint.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP} -exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar "${HOME}/app.war" "$@" diff --git a/src/main/docker/jenkins.yml b/src/main/docker/jenkins.yml deleted file mode 100644 index f0670603..00000000 --- a/src/main/docker/jenkins.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '2' -services: - jenkins: - image: jenkins:latest - ports: - - 49001:8080 - - 50000:50000 - # uncomment for docker in docker - #privileged: true - #volumes: - # enable persistent volume (warning: make sure that the local jenkins_home folder is created) - #- ~/volumes/jenkins_home:/var/jenkins_home - # mount docker sock and binary for docker in docker (only works on linux) - #- /var/run/docker.sock:/var/run/docker.sock - #- /usr/bin/docker:/usr/bin/docker diff --git a/src/main/docker/postgresql.yml b/src/main/docker/postgresql.yml deleted file mode 100644 index 3a872cee..00000000 --- a/src/main/docker/postgresql.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: '2' -services: - hsadminng-postgresql: - image: postgres:10.4 - # volumes: - # - ~/volumes/jhipster/hsadminNg/postgresql/:/var/lib/postgresql/data/ - environment: - - POSTGRES_USER=hsadminNg - - POSTGRES_PASSWORD= - ports: - - 5432:5432 diff --git a/src/main/docker/sonar.yml b/src/main/docker/sonar.yml deleted file mode 100644 index 678facb0..00000000 --- a/src/main/docker/sonar.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: '2' -services: - hsadminng-sonar: - image: sonarqube:7.1 - ports: - - 9001:9000 - - 9092:9092 diff --git a/src/main/docker/swagger-editor.yml b/src/main/docker/swagger-editor.yml deleted file mode 100644 index d2b27f6b..00000000 --- a/src/main/docker/swagger-editor.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: '2' -services: - swagger-editor: - image: swaggerapi/swagger-editor:latest - ports: - - 7742:8080 diff --git a/src/main/java/org/hostsharing/hsadminng/ApplicationWebXml.java b/src/main/java/org/hostsharing/hsadminng/ApplicationWebXml.java deleted file mode 100644 index d6b76fff..00000000 --- a/src/main/java/org/hostsharing/hsadminng/ApplicationWebXml.java +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng; - -import org.hostsharing.hsadminng.config.DefaultProfileUtil; - -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; - -/** - * This is a helper Java class that provides an alternative to creating a web.xml. - * This will be invoked only when the application is deployed to a Servlet container like Tomcat, JBoss etc. - */ -public class ApplicationWebXml extends SpringBootServletInitializer { - - @Override - protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - /** - * set a default to use when no profile is configured. - */ - DefaultProfileUtil.addDefaultProfile(application.application()); - return application.sources(HsadminNgApp.class); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/HsadminNgApp.java b/src/main/java/org/hostsharing/hsadminng/HsadminNgApp.java deleted file mode 100644 index fcb5e760..00000000 --- a/src/main/java/org/hostsharing/hsadminng/HsadminNgApp.java +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng; - -import org.hostsharing.hsadminng.config.ApplicationProperties; -import org.hostsharing.hsadminng.config.DefaultProfileUtil; -import org.hostsharing.hsadminng.service.accessfilter.Role; - -import io.github.jhipster.config.JHipsterConstants; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.core.env.Environment; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.Collection; - -import javax.annotation.PostConstruct; - -@SpringBootApplication -@EnableConfigurationProperties({ LiquibaseProperties.class, ApplicationProperties.class }) -public class HsadminNgApp { - - private static final Logger log = LoggerFactory.getLogger(HsadminNgApp.class); - - private final Environment env; - - public HsadminNgApp(Environment env) { - this.env = env; - - // TODO mhoennig rather use @PostConstruct or something more decentral - Role.init(); - } - - /** - * Initializes hsadminNg. - *

- * Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile - *

- * You can find more information on how profiles work with JHipster on - * https://www.jhipster.tech/profiles/. - */ - @PostConstruct - public void initApplication() { - - Collection activeProfiles = Arrays.asList(env.getActiveProfiles()); - if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) - && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { - log.error( - "You have misconfigured your application! It should not run " + - "with both the 'dev' and 'prod' profiles at the same time."); - } - if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) - && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) { - log.error( - "You have misconfigured your application! It should not " + - "run with both the 'dev' and 'cloud' profiles at the same time."); - } - } - - /** - * Main method, used to run the application. - * - * @param args the command line arguments - */ - public static void main(String[] args) { - SpringApplication app = new SpringApplication(HsadminNgApp.class); - DefaultProfileUtil.addDefaultProfile(app); - Environment env = app.run(args).getEnvironment(); - logApplicationStartup(env); - } - - private static void logApplicationStartup(Environment env) { - String protocol = "http"; - if (env.getProperty("server.ssl.key-store") != null) { - protocol = "https"; - } - String serverPort = env.getProperty("server.port"); - String contextPath = env.getProperty("server.servlet.context-path"); - if (StringUtils.isBlank(contextPath)) { - contextPath = "/"; - } - String hostAddress = "localhost"; - try { - hostAddress = InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException e) { - log.warn("The host name could not be determined, using `localhost` as fallback"); - } - log.info( - "\n----------------------------------------------------------\n\t" + - "Application '{}' is running! Access URLs:\n\t" + - "Local: \t\t{}://localhost:{}{}\n\t" + - "External: \t{}://{}:{}{}\n\t" + - "Profile(s): \t{}\n----------------------------------------------------------", - env.getProperty("spring.application.name"), - protocol, - serverPort, - contextPath, - protocol, - hostAddress, - serverPort, - contextPath, - env.getActiveProfiles()); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/aop/logging/LoggingAspect.java b/src/main/java/org/hostsharing/hsadminng/aop/logging/LoggingAspect.java deleted file mode 100644 index f6574ef4..00000000 --- a/src/main/java/org/hostsharing/hsadminng/aop/logging/LoggingAspect.java +++ /dev/null @@ -1,116 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.aop.logging; - -import io.github.jhipster.config.JHipsterConstants; - -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.AfterThrowing; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.env.Environment; - -import java.util.Arrays; - -/** - * Aspect for logging execution of service and repository Spring components. - * - * By default, it only runs with the "dev" profile. - */ -@Aspect -public class LoggingAspect { - - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - private final Environment env; - - public LoggingAspect(Environment env) { - this.env = env; - } - - /** - * Pointcut that matches all repositories, services and Web REST endpoints. - */ - @Pointcut("within(@org.springframework.stereotype.Repository *)" + - " || within(@org.springframework.stereotype.Service *)" + - " || within(@org.springframework.web.bind.annotation.RestController *)") - public void springBeanPointcut() { - // Method is empty as this is just a Pointcut, the implementations are in the advices. - } - - /** - * Pointcut that matches all Spring beans in the application's main packages. - */ - @Pointcut("within(org.hostsharing.hsadminng.repository..*)" + - " || within(org.hostsharing.hsadminng.service..*)" + - " || within(org.hostsharing.hsadminng.web.rest..*)") - public void applicationPackagePointcut() { - // Method is empty as this is just a Pointcut, the implementations are in the advices. - } - - /** - * Advice that logs methods throwing exceptions. - * - * @param joinPoint join point for advice - * @param e exception - */ - @AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e") - public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { - if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) { - log.error( - "Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName(), - e.getCause() != null ? e.getCause() : "NULL", - e.getMessage(), - e); - - } else { - log.error( - "Exception in {}.{}() with cause = {}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName(), - e.getCause() != null ? e.getCause() : "NULL"); - } - } - - /** - * Advice that logs when a method is entered and exited. - * - * @param joinPoint join point for advice - * @return result - * @throws Throwable throws IllegalArgumentException - */ - @Around("applicationPackagePointcut() && springBeanPointcut()") - public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { - if (log.isDebugEnabled()) { - log.debug( - "Enter: {}.{}() with argument[s] = {}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName(), - Arrays.toString(joinPoint.getArgs())); - } - try { - Object result = joinPoint.proceed(); - if (log.isDebugEnabled()) { - log.debug( - "Exit: {}.{}() with result = {}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName(), - result); - } - return result; - } catch (IllegalArgumentException e) { - log.error( - "Illegal argument: {} in {}.{}()", - Arrays.toString(joinPoint.getArgs()), - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - - throw e; - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/ApplicationProperties.java b/src/main/java/org/hostsharing/hsadminng/config/ApplicationProperties.java deleted file mode 100644 index 19bc4cdb..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/ApplicationProperties.java +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * Properties specific to Hsadmin Ng. - *

- * Properties are configured in the application.yml file. - * See {@link io.github.jhipster.config.JHipsterProperties} for a good example. - */ -@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false) -public class ApplicationProperties { - -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/AsyncConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/AsyncConfiguration.java deleted file mode 100644 index c87c3b44..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/AsyncConfiguration.java +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor; -import io.github.jhipster.config.JHipsterProperties; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; -import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.*; -import org.springframework.scheduling.annotation.SchedulingConfigurer; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import org.springframework.scheduling.config.ScheduledTaskRegistrar; - -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -@Configuration -@EnableAsync -@EnableScheduling -public class AsyncConfiguration implements AsyncConfigurer, SchedulingConfigurer { - - private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class); - - private final JHipsterProperties jHipsterProperties; - - public AsyncConfiguration(JHipsterProperties jHipsterProperties) { - this.jHipsterProperties = jHipsterProperties; - } - - @Override - @Bean(name = "taskExecutor") - public Executor getAsyncExecutor() { - log.debug("Creating Async Task Executor"); - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize()); - executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize()); - executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity()); - executor.setThreadNamePrefix("hsadmin-ng-Executor-"); - return new ExceptionHandlingAsyncTaskExecutor(executor); - } - - @Override - public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { - return new SimpleAsyncUncaughtExceptionHandler(); - } - - @Override - public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { - taskRegistrar.setScheduler(scheduledTaskExecutor()); - } - - @Bean - public Executor scheduledTaskExecutor() { - return Executors.newScheduledThreadPool(jHipsterProperties.getAsync().getCorePoolSize()); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/CacheConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/CacheConfiguration.java deleted file mode 100644 index 02c5479a..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/CacheConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import io.github.jhipster.config.JHipsterProperties; - -import org.ehcache.config.builders.*; -import org.ehcache.jsr107.Eh107Configuration; -import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.*; - -import java.time.Duration; - -@Configuration -@EnableCaching -public class CacheConfiguration { - - private final javax.cache.configuration.Configuration jcacheConfiguration; - - public CacheConfiguration(JHipsterProperties jHipsterProperties) { - JHipsterProperties.Cache.Ehcache ehcache = jHipsterProperties.getCache().getEhcache(); - - jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration( - CacheConfigurationBuilder.newCacheConfigurationBuilder( - Object.class, - Object.class, - ResourcePoolsBuilder.heap(ehcache.getMaxEntries())) - .withExpiry( - ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcache.getTimeToLiveSeconds()))) - .build()); - } - - @Bean - public JCacheManagerCustomizer cacheManagerCustomizer() { - return cm -> { - cm.createCache(org.hostsharing.hsadminng.repository.UserRepository.USERS_BY_LOGIN_CACHE, jcacheConfiguration); - cm.createCache(org.hostsharing.hsadminng.repository.UserRepository.USERS_BY_EMAIL_CACHE, jcacheConfiguration); - // jhipster-needle-ehcache-add-entry - - cm.createCache( - org.hostsharing.hsadminng.repository.UserRoleAssignmentRepository.CURRENT_USER_ROLE_ASSIGNMENTS_CACHE, - jcacheConfiguration); - }; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/CloudDatabaseConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/CloudDatabaseConfiguration.java deleted file mode 100644 index f28cc4b4..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/CloudDatabaseConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import io.github.jhipster.config.JHipsterConstants; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.config.java.AbstractCloudConfig; -import org.springframework.context.annotation.*; - -import javax.sql.DataSource; - -@Configuration -@Profile(JHipsterConstants.SPRING_PROFILE_CLOUD) -public class CloudDatabaseConfiguration extends AbstractCloudConfig { - - private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class); - - private static final String CLOUD_CONFIGURATION_HIKARI_PREFIX = "spring.datasource.hikari"; - - @Bean - @ConfigurationProperties(CLOUD_CONFIGURATION_HIKARI_PREFIX) - public DataSource dataSource() { - log.info("Configuring JDBC datasource from a cloud provider"); - return connectionFactory().dataSource(); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/Constants.java b/src/main/java/org/hostsharing/hsadminng/config/Constants.java deleted file mode 100644 index 82186df3..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/Constants.java +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -/** - * Application constants. - */ -public final class Constants { - - // Regex for acceptable logins - public static final String LOGIN_REGEX = "^[_.@A-Za-z0-9-]*$"; - - public static final String SYSTEM_ACCOUNT = "system"; - public static final String ANONYMOUS_USER = "anonymoususer"; - public static final String DEFAULT_LANGUAGE = "de"; - - private Constants() { - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/DatabaseConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/DatabaseConfiguration.java deleted file mode 100644 index e9a2b893..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/DatabaseConfiguration.java +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import io.github.jhipster.config.JHipsterConstants; -import io.github.jhipster.config.h2.H2ConfigurationHelper; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.core.env.Environment; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -import java.sql.SQLException; - -@Configuration -@EnableJpaRepositories("org.hostsharing.hsadminng.repository") -@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware") -@EnableTransactionManagement -public class DatabaseConfiguration { - - private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class); - - private final Environment env; - - public DatabaseConfiguration(Environment env) { - this.env = env; - } - - /** - * Open the TCP port for the H2 database, so it is available remotely. - * - * @return the H2 database TCP server - * @throws SQLException if the server failed to start - */ - @Bean(initMethod = "start", destroyMethod = "stop") - @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) - public Object h2TCPServer() throws SQLException { - String port = getValidPortForH2(); - log.debug("H2 database is available on port {}", port); - return H2ConfigurationHelper.createServer(port); - } - - private String getValidPortForH2() { - int port = Integer.parseInt(env.getProperty("server.port")); - if (port < 10000) { - port = 10000 + port; - } else { - if (port < 63536) { - port = port + 2000; - } else { - port = port - 2000; - } - } - return String.valueOf(port); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/DateTimeFormatConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/DateTimeFormatConfiguration.java deleted file mode 100644 index fb50c23d..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/DateTimeFormatConfiguration.java +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.format.FormatterRegistry; -import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -/** - * Configure the converters to use the ISO format for dates by default. - */ -@Configuration -public class DateTimeFormatConfiguration implements WebMvcConfigurer { - - @Override - public void addFormatters(FormatterRegistry registry) { - DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); - registrar.setUseIsoFormat(true); - registrar.registerFormatters(registry); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/DefaultProfileUtil.java b/src/main/java/org/hostsharing/hsadminng/config/DefaultProfileUtil.java deleted file mode 100644 index 234b69cb..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/DefaultProfileUtil.java +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import io.github.jhipster.config.JHipsterConstants; - -import org.springframework.boot.SpringApplication; -import org.springframework.core.env.Environment; - -import java.util.*; - -/** - * Utility class to load a Spring profile to be used as default - * when there is no spring.profiles.active set in the environment or as command line argument. - * If the value is not available in application.yml then dev profile will be used as default. - */ -public final class DefaultProfileUtil { - - private static final String SPRING_PROFILE_DEFAULT = "spring.profiles.default"; - - private DefaultProfileUtil() { - } - - /** - * Set a default to use when no profile is configured. - * - * @param app the Spring application - */ - public static void addDefaultProfile(SpringApplication app) { - Map defProperties = new HashMap<>(); - /* - * The default profile to use when no other profiles are defined - * This cannot be set in the application.yml file. - * See https://github.com/spring-projects/spring-boot/issues/1219 - */ - defProperties.put(SPRING_PROFILE_DEFAULT, JHipsterConstants.SPRING_PROFILE_DEVELOPMENT); - app.setDefaultProperties(defProperties); - } - - /** - * Get the profiles that are applied else get default profiles. - * - * @param env spring environment - * @return profiles - */ - public static String[] getActiveProfiles(Environment env) { - String[] profiles = env.getActiveProfiles(); - if (profiles.length == 0) { - return env.getDefaultProfiles(); - } - return profiles; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/JacksonConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/JacksonConfiguration.java deleted file mode 100644 index 0105111b..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/JacksonConfiguration.java +++ /dev/null @@ -1,64 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.zalando.problem.ProblemModule; -import org.zalando.problem.violations.ConstraintViolationProblemModule; - -@Configuration -public class JacksonConfiguration { - - /** - * Support for Java date and time API. - * - * @return the corresponding Jackson module. - */ - @Bean - public JavaTimeModule javaTimeModule() { - return new JavaTimeModule(); - } - - @Bean - public Jdk8Module jdk8TimeModule() { - return new Jdk8Module(); - } - - /* - * Support for Hibernate types in Jackson. - */ - @Bean - public Hibernate5Module hibernate5Module() { - return new Hibernate5Module(); - } - - /* - * Jackson Afterburner module to speed up serialization/deserialization. - */ - @Bean - public AfterburnerModule afterburnerModule() { - return new AfterburnerModule(); - } - - /* - * Module for serialization/deserialization of RFC7807 Problem. - */ - @Bean - ProblemModule problemModule() { - return new ProblemModule(); - } - - /* - * Module for serialization/deserialization of ConstraintViolationProblem. - */ - @Bean - ConstraintViolationProblemModule constraintViolationProblemModule() { - return new ConstraintViolationProblemModule(); - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/LiquibaseConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/LiquibaseConfiguration.java deleted file mode 100644 index 196d907e..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/LiquibaseConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import io.github.jhipster.config.JHipsterConstants; -import io.github.jhipster.config.liquibase.AsyncSpringLiquibase; -import liquibase.integration.spring.SpringLiquibase; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; -import org.springframework.core.task.TaskExecutor; - -import javax.sql.DataSource; - -@Configuration -public class LiquibaseConfiguration { - - private final Logger log = LoggerFactory.getLogger(LiquibaseConfiguration.class); - - private final Environment env; - - public LiquibaseConfiguration(Environment env) { - this.env = env; - } - - @Bean - public SpringLiquibase liquibase( - @Qualifier("taskExecutor") TaskExecutor taskExecutor, - DataSource dataSource, - LiquibaseProperties liquibaseProperties) { - - // Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously - SpringLiquibase liquibase = new AsyncSpringLiquibase(taskExecutor, env); - liquibase.setDataSource(dataSource); - liquibase.setChangeLog("classpath:config/liquibase/master.xml"); - liquibase.setContexts(liquibaseProperties.getContexts()); - liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema()); - liquibase.setDropFirst(liquibaseProperties.isDropFirst()); - liquibase.setChangeLogParameters(liquibaseProperties.getParameters()); - if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) { - liquibase.setShouldRun(false); - } else { - liquibase.setShouldRun(liquibaseProperties.isEnabled()); - log.debug("Configuring Liquibase"); - } - return liquibase; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/LocaleConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/LocaleConfiguration.java deleted file mode 100644 index 99386585..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/LocaleConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import io.github.jhipster.config.locale.AngularCookieLocaleResolver; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.LocaleResolver; -import org.springframework.web.servlet.config.annotation.*; -import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; - -@Configuration -public class LocaleConfiguration implements WebMvcConfigurer { - - @Bean(name = "localeResolver") - public LocaleResolver localeResolver() { - AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver(); - cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY"); - return cookieLocaleResolver; - } - - @Override - public void addInterceptors(InterceptorRegistry registry) { - LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); - localeChangeInterceptor.setParamName("language"); - registry.addInterceptor(localeChangeInterceptor); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/LoggingAspectConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/LoggingAspectConfiguration.java deleted file mode 100644 index 149ea1fa..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/LoggingAspectConfiguration.java +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import org.hostsharing.hsadminng.aop.logging.LoggingAspect; - -import io.github.jhipster.config.JHipsterConstants; - -import org.springframework.context.annotation.*; -import org.springframework.core.env.Environment; - -@Configuration -@EnableAspectJAutoProxy -public class LoggingAspectConfiguration { - - @Bean - @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) - public LoggingAspect loggingAspect(Environment env) { - return new LoggingAspect(env); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/LoggingConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/LoggingConfiguration.java deleted file mode 100644 index 83ecbb1a..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/LoggingConfiguration.java +++ /dev/null @@ -1,160 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import ch.qos.logback.classic.AsyncAppender; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.boolex.OnMarkerEvaluator; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggerContextListener; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.filter.EvaluatorFilter; -import ch.qos.logback.core.spi.ContextAwareBase; -import ch.qos.logback.core.spi.FilterReply; -import io.github.jhipster.config.JHipsterProperties; -import net.logstash.logback.appender.LogstashTcpSocketAppender; -import net.logstash.logback.encoder.LogstashEncoder; -import net.logstash.logback.stacktrace.ShortenedThrowableConverter; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; - -import java.net.InetSocketAddress; -import java.util.Iterator; - -@Configuration -public class LoggingConfiguration { - - private static final String LOGSTASH_APPENDER_NAME = "LOGSTASH"; - - private static final String ASYNC_LOGSTASH_APPENDER_NAME = "ASYNC_LOGSTASH"; - - private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class); - - private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - - private final String appName; - - private final String serverPort; - - private final JHipsterProperties jHipsterProperties; - - public LoggingConfiguration( - @Value("${spring.application.name}") String appName, - @Value("${server.port}") String serverPort, - JHipsterProperties jHipsterProperties) { - this.appName = appName; - this.serverPort = serverPort; - this.jHipsterProperties = jHipsterProperties; - if (jHipsterProperties.getLogging().getLogstash().isEnabled()) { - addLogstashAppender(context); - addContextListener(context); - } - if (jHipsterProperties.getMetrics().getLogs().isEnabled()) { - setMetricsMarkerLogbackFilter(context); - } - } - - private void addContextListener(LoggerContext context) { - LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener(); - loggerContextListener.setContext(context); - context.addListener(loggerContextListener); - } - - private void addLogstashAppender(LoggerContext context) { - log.info("Initializing Logstash logging"); - - LogstashTcpSocketAppender logstashAppender = new LogstashTcpSocketAppender(); - logstashAppender.setName(LOGSTASH_APPENDER_NAME); - logstashAppender.setContext(context); - String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"}"; - - // More documentation is available at: https://github.com/logstash/logstash-logback-encoder - LogstashEncoder logstashEncoder = new LogstashEncoder(); - // Set the Logstash appender config from JHipster properties - logstashAppender.addDestinations( - new InetSocketAddress( - jHipsterProperties.getLogging().getLogstash().getHost(), - jHipsterProperties.getLogging().getLogstash().getPort())); - - ShortenedThrowableConverter throwableConverter = new ShortenedThrowableConverter(); - throwableConverter.setRootCauseFirst(true); - logstashEncoder.setThrowableConverter(throwableConverter); - logstashEncoder.setCustomFields(customFields); - - logstashAppender.setEncoder(logstashEncoder); - logstashAppender.start(); - - // Wrap the appender in an Async appender for performance - AsyncAppender asyncLogstashAppender = new AsyncAppender(); - asyncLogstashAppender.setContext(context); - asyncLogstashAppender.setName(ASYNC_LOGSTASH_APPENDER_NAME); - asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize()); - asyncLogstashAppender.addAppender(logstashAppender); - asyncLogstashAppender.start(); - - context.getLogger("ROOT").addAppender(asyncLogstashAppender); - } - - // Configure a log filter to remove "metrics" logs from all appenders except the "LOGSTASH" appender - private void setMetricsMarkerLogbackFilter(LoggerContext context) { - log.info("Filtering metrics logs from all appenders except the {} appender", LOGSTASH_APPENDER_NAME); - OnMarkerEvaluator onMarkerMetricsEvaluator = new OnMarkerEvaluator(); - onMarkerMetricsEvaluator.setContext(context); - onMarkerMetricsEvaluator.addMarker("metrics"); - onMarkerMetricsEvaluator.start(); - EvaluatorFilter metricsFilter = new EvaluatorFilter<>(); - metricsFilter.setContext(context); - metricsFilter.setEvaluator(onMarkerMetricsEvaluator); - metricsFilter.setOnMatch(FilterReply.DENY); - metricsFilter.start(); - - for (ch.qos.logback.classic.Logger logger : context.getLoggerList()) { - for (Iterator> it = logger.iteratorForAppenders(); it.hasNext();) { - Appender appender = it.next(); - if (!appender.getName().equals(ASYNC_LOGSTASH_APPENDER_NAME)) { - log.debug("Filter metrics logs from the {} appender", appender.getName()); - appender.setContext(context); - appender.addFilter(metricsFilter); - appender.start(); - } - } - } - } - - /** - * Logback configuration is achieved by configuration file and API. - * When configuration file change is detected, the configuration is reset. - * This listener ensures that the programmatic configuration is also re-applied after reset. - */ - class LogbackLoggerContextListener extends ContextAwareBase implements LoggerContextListener { - - @Override - public boolean isResetResistant() { - return true; - } - - @Override - public void onStart(LoggerContext context) { - addLogstashAppender(context); - } - - @Override - public void onReset(LoggerContext context) { - addLogstashAppender(context); - } - - @Override - public void onStop(LoggerContext context) { - // Nothing to do. - } - - @Override - public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) { - // Nothing to do. - } - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/SecurityConfiguration.java b/src/main/java/org/hostsharing/hsadminng/config/SecurityConfiguration.java deleted file mode 100644 index db8a5482..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/SecurityConfiguration.java +++ /dev/null @@ -1,128 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import org.hostsharing.hsadminng.security.*; -import org.hostsharing.hsadminng.security.jwt.*; - -import org.springframework.beans.factory.BeanInitializationException; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.filter.CorsFilter; -import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport; - -import javax.annotation.PostConstruct; - -@Configuration -@EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) -@Import(SecurityProblemSupport.class) -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { - - private final AuthenticationManagerBuilder authenticationManagerBuilder; - - private final UserDetailsService userDetailsService; - - private final TokenProvider tokenProvider; - - private final CorsFilter corsFilter; - - private final SecurityProblemSupport problemSupport; - - public SecurityConfiguration( - AuthenticationManagerBuilder authenticationManagerBuilder, - UserDetailsService userDetailsService, - TokenProvider tokenProvider, - CorsFilter corsFilter, - SecurityProblemSupport problemSupport) { - this.authenticationManagerBuilder = authenticationManagerBuilder; - this.userDetailsService = userDetailsService; - this.tokenProvider = tokenProvider; - this.corsFilter = corsFilter; - this.problemSupport = problemSupport; - } - - @PostConstruct - public void init() { - try { - authenticationManagerBuilder - .userDetailsService(userDetailsService) - .passwordEncoder(passwordEncoder()); - } catch (Exception e) { - throw new BeanInitializationException("Security configuration failed", e); - } - } - - @Override - @Bean - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Override - public void configure(WebSecurity web) throws Exception { - web.ignoring() - .antMatchers(HttpMethod.OPTIONS, "/**") - .antMatchers("/app/**/*.{js,html}") - .antMatchers("/i18n/**") - .antMatchers("/content/**") - .antMatchers("/h2-console/**") - .antMatchers("/swagger-ui/index.html") - .antMatchers("/test/**"); - } - - @Override - public void configure(HttpSecurity http) throws Exception { - // @formatter:off - http - .csrf() - .disable() - .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) - .exceptionHandling() - .authenticationEntryPoint(problemSupport) - .accessDeniedHandler(problemSupport) - .and() - .headers() - .frameOptions() - .disable() - .and() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() - .authorizeRequests() - .antMatchers("/api/register").permitAll() - .antMatchers("/api/activate").permitAll() - .antMatchers("/api/authenticate").permitAll() - .antMatchers("/api/account/reset-password/init").permitAll() - .antMatchers("/api/account/reset-password/finish").permitAll() - .antMatchers("/api/**").authenticated() - .antMatchers("/management/health").permitAll() - .antMatchers("/management/info").permitAll() - .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN) - .and() - .apply(securityConfigurerAdapter()); - // @formatter:on - } - - private JWTConfigurer securityConfigurerAdapter() { - return new JWTConfigurer(tokenProvider); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/WebConfigurer.java b/src/main/java/org/hostsharing/hsadminng/config/WebConfigurer.java deleted file mode 100644 index e0265aff..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/WebConfigurer.java +++ /dev/null @@ -1,172 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import static java.net.URLDecoder.decode; - -import io.github.jhipster.config.JHipsterConstants; -import io.github.jhipster.config.JHipsterProperties; -import io.github.jhipster.config.h2.H2ConfigurationHelper; -import io.github.jhipster.web.filter.CachingHttpHeadersFilter; -import io.undertow.UndertowOptions; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; -import org.springframework.boot.web.server.*; -import org.springframework.boot.web.servlet.ServletContextInitializer; -import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; -import org.springframework.http.MediaType; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -import java.io.File; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.*; - -import javax.servlet.*; - -/** - * Configuration of web application with Servlet 3.0 APIs. - */ -@Configuration -public class WebConfigurer implements ServletContextInitializer, WebServerFactoryCustomizer { - - private final Logger log = LoggerFactory.getLogger(WebConfigurer.class); - - private final Environment env; - - private final JHipsterProperties jHipsterProperties; - - public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) { - - this.env = env; - this.jHipsterProperties = jHipsterProperties; - } - - @Override - public void onStartup(ServletContext servletContext) throws ServletException { - if (env.getActiveProfiles().length != 0) { - log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles()); - } - EnumSet disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC); - if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { - initCachingHttpHeadersFilter(servletContext, disps); - } - if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) { - initH2Console(servletContext); - } - log.info("Web application fully configured"); - } - - /** - * Customize the Servlet engine: Mime types, the document root, the cache. - */ - @Override - public void customize(WebServerFactory server) { - setMimeMappings(server); - // When running in an IDE or with ./gradlew bootRun, set location of the static web assets. - setLocationForStaticAssets(server); - - /* - * Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288 - * HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1. - * See the JHipsterProperties class and your application-*.yml configuration files - * for more information. - */ - if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) && - server instanceof UndertowServletWebServerFactory) { - - ((UndertowServletWebServerFactory) server) - .addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true)); - } - } - - private void setMimeMappings(WebServerFactory server) { - if (server instanceof ConfigurableServletWebServerFactory) { - MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT); - // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711 - mappings.add("html", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase()); - // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64 - mappings.add("json", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase()); - ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server; - servletWebServer.setMimeMappings(mappings); - } - } - - private void setLocationForStaticAssets(WebServerFactory server) { - if (server instanceof ConfigurableServletWebServerFactory) { - ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server; - File root; - String prefixPath = resolvePathPrefix(); - root = new File(prefixPath + "build/www/"); - if (root.exists() && root.isDirectory()) { - servletWebServer.setDocumentRoot(root); - } - } - } - - /** - * Resolve path prefix to static resources. - */ - private String resolvePathPrefix() { - String fullExecutablePath; - try { - fullExecutablePath = decode(this.getClass().getResource("").getPath(), StandardCharsets.UTF_8.name()); - } catch (UnsupportedEncodingException e) { - /* try without decoding if this ever happens */ - fullExecutablePath = this.getClass().getResource("").getPath(); - } - String rootPath = Paths.get(".").toUri().normalize().getPath(); - String extractedPath = fullExecutablePath.replace(rootPath, ""); - int extractionEndIndex = extractedPath.indexOf("build/"); - if (extractionEndIndex <= 0) { - return ""; - } - return extractedPath.substring(0, extractionEndIndex); - } - - /** - * Initializes the caching HTTP Headers Filter. - */ - private void initCachingHttpHeadersFilter( - ServletContext servletContext, - EnumSet disps) { - log.debug("Registering Caching HTTP Headers Filter"); - FilterRegistration.Dynamic cachingHttpHeadersFilter = servletContext.addFilter( - "cachingHttpHeadersFilter", - new CachingHttpHeadersFilter(jHipsterProperties)); - - cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/i18n/*"); - cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/content/*"); - cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/app/*"); - cachingHttpHeadersFilter.setAsyncSupported(true); - } - - @Bean - public CorsFilter corsFilter() { - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - CorsConfiguration config = jHipsterProperties.getCors(); - if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) { - log.debug("Registering CORS filter"); - source.registerCorsConfiguration("/api/**", config); - source.registerCorsConfiguration("/management/**", config); - source.registerCorsConfiguration("/v2/api-docs", config); - } - return new CorsFilter(source); - } - - /** - * Initializes H2 console. - */ - private void initH2Console(ServletContext servletContext) { - log.debug("Initialize H2 console"); - H2ConfigurationHelper.initH2Console(servletContext); - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/audit/AuditEventConverter.java b/src/main/java/org/hostsharing/hsadminng/config/audit/AuditEventConverter.java deleted file mode 100644 index 9a591c96..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/audit/AuditEventConverter.java +++ /dev/null @@ -1,90 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config.audit; - -import org.hostsharing.hsadminng.domain.PersistentAuditEvent; - -import org.springframework.boot.actuate.audit.AuditEvent; -import org.springframework.security.web.authentication.WebAuthenticationDetails; -import org.springframework.stereotype.Component; - -import java.util.*; - -@Component -public class AuditEventConverter { - - /** - * Convert a list of PersistentAuditEvent to a list of AuditEvent - * - * @param persistentAuditEvents the list to convert - * @return the converted list. - */ - public List convertToAuditEvent(Iterable persistentAuditEvents) { - if (persistentAuditEvents == null) { - return Collections.emptyList(); - } - List auditEvents = new ArrayList<>(); - for (PersistentAuditEvent persistentAuditEvent : persistentAuditEvents) { - auditEvents.add(convertToAuditEvent(persistentAuditEvent)); - } - return auditEvents; - } - - /** - * Convert a PersistentAuditEvent to an AuditEvent - * - * @param persistentAuditEvent the event to convert - * @return the converted list. - */ - public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) { - if (persistentAuditEvent == null) { - return null; - } - return new AuditEvent( - persistentAuditEvent.getAuditEventDate(), - persistentAuditEvent.getPrincipal(), - persistentAuditEvent.getAuditEventType(), - convertDataToObjects(persistentAuditEvent.getData())); - } - - /** - * Internal conversion. This is needed to support the current SpringBoot actuator AuditEventRepository interface - * - * @param data the data to convert - * @return a map of String, Object - */ - public Map convertDataToObjects(Map data) { - Map results = new HashMap<>(); - - if (data != null) { - for (Map.Entry entry : data.entrySet()) { - results.put(entry.getKey(), entry.getValue()); - } - } - return results; - } - - /** - * Internal conversion. This method will allow to save additional data. - * By default, it will save the object as string - * - * @param data the data to convert - * @return a map of String, String - */ - public Map convertDataToStrings(Map data) { - Map results = new HashMap<>(); - - if (data != null) { - for (Map.Entry entry : data.entrySet()) { - // Extract the data that will be saved. - if (entry.getValue() instanceof WebAuthenticationDetails) { - WebAuthenticationDetails authenticationDetails = (WebAuthenticationDetails) entry.getValue(); - results.put("remoteAddress", authenticationDetails.getRemoteAddress()); - results.put("sessionId", authenticationDetails.getSessionId()); - } else { - results.put(entry.getKey(), Objects.toString(entry.getValue())); - } - } - } - return results; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/config/audit/package-info.java b/src/main/java/org/hostsharing/hsadminng/config/audit/package-info.java deleted file mode 100644 index aa7e7d25..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/audit/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Audit specific code. - */ -package org.hostsharing.hsadminng.config.audit; diff --git a/src/main/java/org/hostsharing/hsadminng/config/package-info.java b/src/main/java/org/hostsharing/hsadminng/config/package-info.java deleted file mode 100644 index aaeaef0a..00000000 --- a/src/main/java/org/hostsharing/hsadminng/config/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Spring Framework configuration files. - */ -package org.hostsharing.hsadminng.config; diff --git a/src/main/java/org/hostsharing/hsadminng/domain/AbstractAuditingEntity.java b/src/main/java/org/hostsharing/hsadminng/domain/AbstractAuditingEntity.java deleted file mode 100644 index d1f27399..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/AbstractAuditingEntity.java +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import org.hibernate.envers.Audited; -import org.springframework.data.annotation.CreatedBy; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -import java.io.Serializable; -import java.time.Instant; - -import javax.persistence.Column; -import javax.persistence.EntityListeners; -import javax.persistence.MappedSuperclass; - -/** - * Base abstract class for entities which will hold definitions for created, last modified by and created, - * last modified by date. - */ -@MappedSuperclass -@Audited -@EntityListeners(AuditingEntityListener.class) -public abstract class AbstractAuditingEntity implements Serializable { - - private static final long serialVersionUID = 1L; - - @CreatedBy - @Column(name = "created_by", nullable = false, length = 50, updatable = false) - @JsonIgnore - private String createdBy; - - @CreatedDate - @Column(name = "created_date", updatable = false) - @JsonIgnore - private Instant createdDate = Instant.now(); - - @LastModifiedBy - @Column(name = "last_modified_by", length = 50) - @JsonIgnore - private String lastModifiedBy; - - @LastModifiedDate - @Column(name = "last_modified_date") - @JsonIgnore - private Instant lastModifiedDate = Instant.now(); - - public String getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(String createdBy) { - this.createdBy = createdBy; - } - - public Instant getCreatedDate() { - return createdDate; - } - - public void setCreatedDate(Instant createdDate) { - this.createdDate = createdDate; - } - - public String getLastModifiedBy() { - return lastModifiedBy; - } - - public void setLastModifiedBy(String lastModifiedBy) { - this.lastModifiedBy = lastModifiedBy; - } - - public Instant getLastModifiedDate() { - return lastModifiedDate; - } - - public void setLastModifiedDate(Instant lastModifiedDate) { - this.lastModifiedDate = lastModifiedDate; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/Asset.java b/src/main/java/org/hostsharing/hsadminng/domain/Asset.java deleted file mode 100644 index db985858..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/Asset.java +++ /dev/null @@ -1,184 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.Objects; - -import javax.persistence.*; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * A Asset. - */ -@Entity -@Table(name = "asset") -public class Asset implements Serializable { - - private static final long serialVersionUID = 1L; - - public static final String ENTITY_NAME = "asset"; - public static final String ENTITY_TYPE_ID = "customer.asset"; - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") - @SequenceGenerator(name = "sequenceGenerator") - private Long id; - - @NotNull - @Column(name = "document_date", nullable = false) - private LocalDate documentDate; - - @NotNull - @Column(name = "value_date", nullable = false) - private LocalDate valueDate; - - @NotNull - @Enumerated(EnumType.STRING) - @Column(name = "action", nullable = false) - private AssetAction action; - - @NotNull - @Column(name = "amount", precision = 10, scale = 2, nullable = false) - private BigDecimal amount; - - @Size(max = 160) - @Column(name = "remark", length = 160) - private String remark; - - @ManyToOne(optional = false) - @NotNull - @JsonIgnoreProperties("assets") - private Membership membership; - - // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove - public Long getId() { - return id; - } - - public Asset id(Long id) { - this.id = id; - return this; - } - - public void setId(Long id) { - this.id = id; - } - - public LocalDate getDocumentDate() { - return documentDate; - } - - public Asset documentDate(LocalDate documentDate) { - this.documentDate = documentDate; - return this; - } - - public void setDocumentDate(LocalDate documentDate) { - this.documentDate = documentDate; - } - - public LocalDate getValueDate() { - return valueDate; - } - - public Asset valueDate(LocalDate valueDate) { - this.valueDate = valueDate; - return this; - } - - public void setValueDate(LocalDate valueDate) { - this.valueDate = valueDate; - } - - public AssetAction getAction() { - return action; - } - - public Asset action(AssetAction action) { - this.action = action; - return this; - } - - public void setAction(AssetAction action) { - this.action = action; - } - - public BigDecimal getAmount() { - return amount; - } - - public Asset amount(BigDecimal amount) { - this.amount = amount; - return this; - } - - public void setAmount(BigDecimal amount) { - this.amount = amount; - } - - public String getRemark() { - return remark; - } - - public Asset remark(String remark) { - this.remark = remark; - return this; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Membership getMembership() { - return membership; - } - - public Asset membership(Membership membership) { - this.membership = membership; - return this; - } - - public void setMembership(Membership membership) { - this.membership = membership; - } - // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Asset asset = (Asset) o; - if (asset.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), asset.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "Asset{" + - "id=" + getId() + - ", documentDate='" + getDocumentDate() + "'" + - ", valueDate='" + getValueDate() + "'" + - ", action='" + getAction() + "'" + - ", amount=" + getAmount() + - ", remark='" + getRemark() + "'" + - "}"; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/Authority.java b/src/main/java/org/hostsharing/hsadminng/domain/Authority.java deleted file mode 100644 index 422347c7..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/Authority.java +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import java.io.Serializable; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * An authority (a security role) used by Spring Security. - */ -@Entity -@Table(name = "jhi_authority") -public class Authority implements Serializable { - - private static final long serialVersionUID = 1L; - - @NotNull - @Size(max = 50) - @Id - @Column(length = 50) - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - Authority authority = (Authority) o; - - return !(name != null ? !name.equals(authority.name) : authority.name != null); - } - - @Override - public int hashCode() { - return name != null ? name.hashCode() : 0; - } - - @Override - public String toString() { - return "Authority{" + - "name='" + name + '\'' + - "}"; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/Customer.java b/src/main/java/org/hostsharing/hsadminng/domain/Customer.java deleted file mode 100644 index aefb1fcf..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/Customer.java +++ /dev/null @@ -1,408 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import org.hostsharing.hsadminng.domain.enumeration.CustomerKind; -import org.hostsharing.hsadminng.domain.enumeration.VatRegion; - -import java.io.Serializable; -import java.time.LocalDate; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import javax.persistence.*; -import javax.validation.constraints.*; - -/** - * A Customer. - */ -@Entity -@Table(name = "customer") -public class Customer implements Serializable { - - private static final long serialVersionUID = 1L; - - public static final String ENTITY_TYPE_ID = "customer.Customer"; - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") - @SequenceGenerator(name = "sequenceGenerator") - private Long id; - - @NotNull - @Min(value = 10000) - @Max(value = 99999) - @Column(name = "reference", nullable = false, unique = true) - private Integer reference; - - @NotNull - @Size(max = 3) - @Pattern(regexp = "[a-z][a-z0-9]+") - @Column(name = "prefix", length = 3, nullable = false, unique = true) - private String prefix; - - @NotNull - @Size(max = 80) - @Column(name = "name", length = 80, nullable = false) - private String name; - - @NotNull - @Enumerated(EnumType.STRING) - @Column(name = "kind", nullable = false) - private CustomerKind kind; - - @Column(name = "birth_date") - private LocalDate birthDate; - - @Size(max = 80) - @Column(name = "birth_place", length = 80) - private String birthPlace; - - @Size(max = 80) - @Column(name = "registration_court", length = 80) - private String registrationCourt; - - @Size(max = 80) - @Column(name = "registration_number", length = 80) - private String registrationNumber; - - @NotNull - @Enumerated(EnumType.STRING) - @Column(name = "vat_region", nullable = false) - private VatRegion vatRegion; - - @Size(max = 40) - @Column(name = "vat_number", length = 40) - private String vatNumber; - - @Size(max = 80) - @Column(name = "contractual_salutation", length = 80) - private String contractualSalutation; - - @NotNull - @Size(max = 400) - @Column(name = "contractual_address", length = 400, nullable = false) - private String contractualAddress; - - @Size(max = 80) - @Column(name = "billing_salutation", length = 80) - private String billingSalutation; - - @Size(max = 400) - @Column(name = "billing_address", length = 400) - private String billingAddress; - - @Size(max = 160) - @Column(name = "remark", length = 160) - private String remark; - - @OneToMany(mappedBy = "customer") - private Set memberships = new HashSet<>(); - - @OneToMany(mappedBy = "customer") - private Set sepamandates = new HashSet<>(); - - // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove - - public Long getId() { - return id; - } - - public Customer id(long id) { - this.id = id; - return this; - } - - public void setId(Long id) { - this.id = id; - } - - public Integer getReference() { - return reference; - } - - public Customer reference(Integer reference) { - this.reference = reference; - return this; - } - - public void setReference(Integer reference) { - this.reference = reference; - } - - public String getPrefix() { - return prefix; - } - - public Customer prefix(String prefix) { - this.prefix = prefix; - return this; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public String getName() { - return name; - } - - public Customer name(String name) { - this.name = name; - return this; - } - - public void setName(String name) { - this.name = name; - } - - public CustomerKind getKind() { - return kind; - } - - public Customer kind(CustomerKind kind) { - this.kind = kind; - return this; - } - - public void setKind(CustomerKind kind) { - this.kind = kind; - } - - public LocalDate getBirthDate() { - return birthDate; - } - - public Customer birthDate(LocalDate birthDate) { - this.birthDate = birthDate; - return this; - } - - public void setBirthDate(LocalDate birthDate) { - this.birthDate = birthDate; - } - - public String getBirthPlace() { - return birthPlace; - } - - public Customer birthPlace(String birthPlace) { - this.birthPlace = birthPlace; - return this; - } - - public void setBirthPlace(String birthPlace) { - this.birthPlace = birthPlace; - } - - public String getRegistrationCourt() { - return registrationCourt; - } - - public Customer registrationCourt(String registrationCourt) { - this.registrationCourt = registrationCourt; - return this; - } - - public void setRegistrationCourt(String registrationCourt) { - this.registrationCourt = registrationCourt; - } - - public String getRegistrationNumber() { - return registrationNumber; - } - - public Customer registrationNumber(String registrationNumber) { - this.registrationNumber = registrationNumber; - return this; - } - - public void setRegistrationNumber(String registrationNumber) { - this.registrationNumber = registrationNumber; - } - - public VatRegion getVatRegion() { - return vatRegion; - } - - public Customer vatRegion(VatRegion vatRegion) { - this.vatRegion = vatRegion; - return this; - } - - public void setVatRegion(VatRegion vatRegion) { - this.vatRegion = vatRegion; - } - - public String getVatNumber() { - return vatNumber; - } - - public Customer vatNumber(String vatNumber) { - this.vatNumber = vatNumber; - return this; - } - - public void setVatNumber(String vatNumber) { - this.vatNumber = vatNumber; - } - - public String getContractualSalutation() { - return contractualSalutation; - } - - public Customer contractualSalutation(String contractualSalutation) { - this.contractualSalutation = contractualSalutation; - return this; - } - - public void setContractualSalutation(String contractualSalutation) { - this.contractualSalutation = contractualSalutation; - } - - public String getContractualAddress() { - return contractualAddress; - } - - public Customer contractualAddress(String contractualAddress) { - this.contractualAddress = contractualAddress; - return this; - } - - public void setContractualAddress(String contractualAddress) { - this.contractualAddress = contractualAddress; - } - - public String getBillingSalutation() { - return billingSalutation; - } - - public Customer billingSalutation(String billingSalutation) { - this.billingSalutation = billingSalutation; - return this; - } - - public void setBillingSalutation(String billingSalutation) { - this.billingSalutation = billingSalutation; - } - - public String getBillingAddress() { - return billingAddress; - } - - public Customer billingAddress(String billingAddress) { - this.billingAddress = billingAddress; - return this; - } - - public void setBillingAddress(String billingAddress) { - this.billingAddress = billingAddress; - } - - public String getRemark() { - return remark; - } - - public Customer remark(String remark) { - this.remark = remark; - return this; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Set getMemberships() { - return memberships; - } - - public Customer memberships(Set memberships) { - this.memberships = memberships; - return this; - } - - public Customer addMembership(Membership membership) { - this.memberships.add(membership); - membership.setCustomer(this); - return this; - } - - public Customer removeMembership(Membership membership) { - this.memberships.remove(membership); - membership.setCustomer(null); - return this; - } - - public void setMemberships(Set memberships) { - this.memberships = memberships; - } - - public Set getSepamandates() { - return sepamandates; - } - - public Customer sepamandates(Set sepaMandates) { - this.sepamandates = sepaMandates; - return this; - } - - public Customer addSepamandate(SepaMandate sepaMandate) { - this.sepamandates.add(sepaMandate); - sepaMandate.setCustomer(this); - return this; - } - - public Customer removeSepamandate(SepaMandate sepaMandate) { - this.sepamandates.remove(sepaMandate); - sepaMandate.setCustomer(null); - return this; - } - - public void setSepamandates(Set sepaMandates) { - this.sepamandates = sepaMandates; - } - - // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Customer customer = (Customer) o; - if (customer.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), customer.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "Customer{" + - "id=" + getId() + - ", reference=" + getReference() + - ", prefix='" + getPrefix() + "'" + - ", name='" + getName() + "'" + - ", kind='" + getKind() + "'" + - ", birthDate='" + getBirthDate() + "'" + - ", birthPlace='" + getBirthPlace() + "'" + - ", registrationCourt='" + getRegistrationCourt() + "'" + - ", registrationNumber='" + getRegistrationNumber() + "'" + - ", vatRegion='" + getVatRegion() + "'" + - ", vatNumber='" + getVatNumber() + "'" + - ", contractualSalutation='" + getContractualSalutation() + "'" + - ", contractualAddress='" + getContractualAddress() + "'" + - ", billingSalutation='" + getBillingSalutation() + "'" + - ", billingAddress='" + getBillingAddress() + "'" + - ", remark='" + getRemark() + "'" + - "}"; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/Membership.java b/src/main/java/org/hostsharing/hsadminng/domain/Membership.java deleted file mode 100644 index 36cbdc00..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/Membership.java +++ /dev/null @@ -1,238 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -import java.io.Serializable; -import java.time.LocalDate; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import javax.persistence.*; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * A Membership. - */ -@Entity -@Table(name = "membership") -public class Membership implements Serializable { - - private static final long serialVersionUID = 1L; - - public static final String ENTITY_NAME = "membership"; - public static final String ENTITY_TYPE_ID = "customer.Membership"; - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") - @SequenceGenerator(name = "sequenceGenerator") - private Long id; - - @NotNull - @Column(name = "admission_document_date", nullable = false) - private LocalDate admissionDocumentDate; - - @Column(name = "cancellation_document_date") - private LocalDate cancellationDocumentDate; - - @NotNull - @Column(name = "member_from_date", nullable = false) - private LocalDate memberFromDate; - - @Column(name = "member_until_date") - private LocalDate memberUntilDate; - - @Size(max = 160) - @Column(name = "remark", length = 160) - private String remark; - - @OneToMany(mappedBy = "membership") - private Set shares = new HashSet<>(); - - @OneToMany(mappedBy = "membership") - private Set assets = new HashSet<>(); - - @ManyToOne(optional = false) - @NotNull - @JsonIgnoreProperties("memberships") - private Customer customer; - - // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove - - public Long getId() { - return id; - } - - public Membership id(Long id) { - this.id = id; - return this; - } - - public void setId(Long id) { - this.id = id; - } - - public LocalDate getAdmissionDocumentDate() { - return admissionDocumentDate; - } - - public Membership admissionDocumentDate(LocalDate admissionDocumentDate) { - this.admissionDocumentDate = admissionDocumentDate; - return this; - } - - public void setAdmissionDocumentDate(LocalDate admissionDocumentDate) { - this.admissionDocumentDate = admissionDocumentDate; - } - - public LocalDate getCancellationDocumentDate() { - return cancellationDocumentDate; - } - - public Membership cancellationDocumentDate(LocalDate cancellationDocumentDate) { - this.cancellationDocumentDate = cancellationDocumentDate; - return this; - } - - public void setCancellationDocumentDate(LocalDate cancellationDocumentDate) { - this.cancellationDocumentDate = cancellationDocumentDate; - } - - public LocalDate getMemberFromDate() { - return memberFromDate; - } - - public Membership memberFromDate(LocalDate memberFromDate) { - this.memberFromDate = memberFromDate; - return this; - } - - public void setMemberFromDate(LocalDate memberFromDate) { - this.memberFromDate = memberFromDate; - } - - public LocalDate getMemberUntilDate() { - return memberUntilDate; - } - - public Membership memberUntilDate(LocalDate memberUntilDate) { - this.memberUntilDate = memberUntilDate; - return this; - } - - public void setMemberUntilDate(LocalDate memberUntilDate) { - this.memberUntilDate = memberUntilDate; - } - - public String getRemark() { - return remark; - } - - public Membership remark(String remark) { - this.remark = remark; - return this; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Set getShares() { - return shares; - } - - public Membership shares(Set shares) { - this.shares = shares; - return this; - } - - public Membership addShare(Share share) { - this.shares.add(share); - share.setMembership(this); - return this; - } - - public Membership removeShare(Share share) { - this.shares.remove(share); - share.setMembership(null); - return this; - } - - public void setShares(Set shares) { - this.shares = shares; - } - - public Set getAssets() { - return assets; - } - - public Membership assets(Set assets) { - this.assets = assets; - return this; - } - - public Membership addAsset(Asset asset) { - this.assets.add(asset); - asset.setMembership(this); - return this; - } - - public Membership removeAsset(Asset asset) { - this.assets.remove(asset); - asset.setMembership(null); - return this; - } - - public void setAssets(Set assets) { - this.assets = assets; - } - - public Customer getCustomer() { - return customer; - } - - public Membership customer(Customer customer) { - this.customer = customer; - return this; - } - - public void setCustomer(Customer customer) { - this.customer = customer; - } - - // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Membership membership = (Membership) o; - if (membership.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), membership.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "Membership{" + - "id=" + getId() + - ", admissionDocumentDate='" + getAdmissionDocumentDate() + "'" + - ", cancellationDocumentDate='" + getCancellationDocumentDate() + "'" + - ", memberFromDate='" + getMemberFromDate() + "'" + - ", memberUntilDate='" + getMemberUntilDate() + "'" + - ", remark='" + getRemark() + "'" + - "}"; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/PersistentAuditEvent.java b/src/main/java/org/hostsharing/hsadminng/domain/PersistentAuditEvent.java deleted file mode 100644 index d59463c2..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/PersistentAuditEvent.java +++ /dev/null @@ -1,113 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import java.io.Serializable; -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -import javax.persistence.*; -import javax.validation.constraints.NotNull; - -/** - * Persist AuditEvent managed by the Spring Boot actuator. - * - * @see org.springframework.boot.actuate.audit.AuditEvent - */ -@Entity -@Table(name = "jhi_persistent_audit_event") -public class PersistentAuditEvent implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") - @SequenceGenerator(name = "sequenceGenerator") - @Column(name = "event_id") - private Long id; - - @NotNull - @Column(nullable = false) - private String principal; - - @Column(name = "event_date") - private Instant auditEventDate; - - @Column(name = "event_type") - private String auditEventType; - - @ElementCollection - @MapKeyColumn(name = "name") - @Column(name = "value") - @CollectionTable(name = "jhi_persistent_audit_evt_data", joinColumns = @JoinColumn(name = "event_id")) - private Map data = new HashMap<>(); - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getPrincipal() { - return principal; - } - - public void setPrincipal(String principal) { - this.principal = principal; - } - - public Instant getAuditEventDate() { - return auditEventDate; - } - - public void setAuditEventDate(Instant auditEventDate) { - this.auditEventDate = auditEventDate; - } - - public String getAuditEventType() { - return auditEventType; - } - - public void setAuditEventType(String auditEventType) { - this.auditEventType = auditEventType; - } - - public Map getData() { - return data; - } - - public void setData(Map data) { - this.data = data; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - PersistentAuditEvent persistentAuditEvent = (PersistentAuditEvent) o; - return !(persistentAuditEvent.getId() == null || getId() == null) - && Objects.equals(getId(), persistentAuditEvent.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "PersistentAuditEvent{" + - "principal='" + principal + '\'' + - ", auditEventDate=" + auditEventDate + - ", auditEventType='" + auditEventType + '\'' + - '}'; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/SepaMandate.java b/src/main/java/org/hostsharing/hsadminng/domain/SepaMandate.java deleted file mode 100644 index 16b849d9..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/SepaMandate.java +++ /dev/null @@ -1,250 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -import java.io.Serializable; -import java.time.LocalDate; -import java.util.Objects; - -import javax.persistence.*; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * A SepaMandate. - */ -@Entity -@Table(name = "sepa_mandate") -public class SepaMandate implements Serializable { - - private static final long serialVersionUID = 1L; - - public static final String ENTITY_TYPE_ID = "customer.SepaMandate"; - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") - @SequenceGenerator(name = "sequenceGenerator") - private Long id; - - @NotNull - @Size(max = 40) - @Column(name = "reference", length = 40, nullable = false, unique = true) - private String reference; - - @Size(max = 34) - @Column(name = "iban", length = 34) - private String iban; - - @Size(max = 11) - @Column(name = "bic", length = 11) - private String bic; - - @NotNull - @Column(name = "granting_document_date", nullable = false) - private LocalDate grantingDocumentDate; - - @Column(name = "revokation_document_date") - private LocalDate revokationDocumentDate; - - @NotNull - @Column(name = "valid_from_date", nullable = false) - private LocalDate validFromDate; - - @Column(name = "valid_until_date") - private LocalDate validUntilDate; - - @Column(name = "last_used_date") - private LocalDate lastUsedDate; - - @Size(max = 160) - @Column(name = "remark", length = 160) - private String remark; - - @ManyToOne(optional = false) - @NotNull - @JsonIgnoreProperties("sepamandates") - private Customer customer; - - // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove - - public Long getId() { - return id; - } - - public SepaMandate id(final Long id) { - this.id = id; - return this; - } - - public void setId(Long id) { - this.id = id; - } - - public String getReference() { - return reference; - } - - public SepaMandate reference(String reference) { - this.reference = reference; - return this; - } - - public void setReference(String reference) { - this.reference = reference; - } - - public String getIban() { - return iban; - } - - public SepaMandate iban(String iban) { - this.iban = iban; - return this; - } - - public void setIban(String iban) { - this.iban = iban; - } - - public String getBic() { - return bic; - } - - public SepaMandate bic(String bic) { - this.bic = bic; - return this; - } - - public void setBic(String bic) { - this.bic = bic; - } - - public LocalDate getGrantingDocumentDate() { - return grantingDocumentDate; - } - - public SepaMandate grantingDocumentDate(LocalDate grantingDocumentDate) { - this.grantingDocumentDate = grantingDocumentDate; - return this; - } - - public void setGrantingDocumentDate(LocalDate grantingDocumentDate) { - this.grantingDocumentDate = grantingDocumentDate; - } - - public LocalDate getRevokationDocumentDate() { - return revokationDocumentDate; - } - - public SepaMandate revokationDocumentDate(LocalDate revokationDocumentDate) { - this.revokationDocumentDate = revokationDocumentDate; - return this; - } - - public void setRevokationDocumentDate(LocalDate revokationDocumentDate) { - this.revokationDocumentDate = revokationDocumentDate; - } - - public LocalDate getValidFromDate() { - return validFromDate; - } - - public SepaMandate validFromDate(LocalDate validFromDate) { - this.validFromDate = validFromDate; - return this; - } - - public void setValidFromDate(LocalDate validFromDate) { - this.validFromDate = validFromDate; - } - - public LocalDate getValidUntilDate() { - return validUntilDate; - } - - public SepaMandate validUntilDate(LocalDate validUntilDate) { - this.validUntilDate = validUntilDate; - return this; - } - - public void setValidUntilDate(LocalDate validUntilDate) { - this.validUntilDate = validUntilDate; - } - - public LocalDate getLastUsedDate() { - return lastUsedDate; - } - - public SepaMandate lastUsedDate(LocalDate lastUsedDate) { - this.lastUsedDate = lastUsedDate; - return this; - } - - public void setLastUsedDate(LocalDate lastUsedDate) { - this.lastUsedDate = lastUsedDate; - } - - public String getRemark() { - return remark; - } - - public SepaMandate remark(String remark) { - this.remark = remark; - return this; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Customer getCustomer() { - return customer; - } - - public SepaMandate customer(Customer customer) { - this.customer = customer; - return this; - } - - public void setCustomer(Customer customer) { - this.customer = customer; - } - // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SepaMandate sepaMandate = (SepaMandate) o; - if (sepaMandate.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), sepaMandate.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "SepaMandate{" + - "id=" + getId() + - ", reference='" + getReference() + "'" + - ", iban='" + getIban() + "'" + - ", bic='" + getBic() + "'" + - ", grantingDocumentDate='" + getGrantingDocumentDate() + "'" + - ", revokationDocumentDate='" + getRevokationDocumentDate() + "'" + - ", validFromDate='" + getValidFromDate() + "'" + - ", validUntilDate='" + getValidUntilDate() + "'" + - ", lastUsedDate='" + getLastUsedDate() + "'" + - ", remark='" + getRemark() + "'" + - "}"; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/Share.java b/src/main/java/org/hostsharing/hsadminng/domain/Share.java deleted file mode 100644 index 3aa6e10c..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/Share.java +++ /dev/null @@ -1,183 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -import java.io.Serializable; -import java.time.LocalDate; -import java.util.Objects; - -import javax.persistence.*; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * A Share. - */ -@Entity -@Table(name = "share") -public class Share implements Serializable { - - private static final long serialVersionUID = 1L; - - public static final String ENTITY_NAME = "share"; - public static final String ENTITY_TYPE_ID = "customer.share"; - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") - @SequenceGenerator(name = "sequenceGenerator") - private Long id; - - @NotNull - @Column(name = "document_date", nullable = false) - private LocalDate documentDate; - - @NotNull - @Column(name = "value_date", nullable = false) - private LocalDate valueDate; - - @NotNull - @Enumerated(EnumType.STRING) - @Column(name = "action", nullable = false) - private ShareAction action; - - @NotNull - @Column(name = "quantity", nullable = false) - private Integer quantity; - - @Size(max = 160) - @Column(name = "remark", length = 160) - private String remark; - - @ManyToOne(optional = false) - @NotNull - @JsonIgnoreProperties("shares") - private Membership membership; - - // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove - public Long getId() { - return id; - } - - public Share id(final Long id) { - this.id = id; - return this; - } - - public void setId(Long id) { - this.id = id; - } - - public LocalDate getDocumentDate() { - return documentDate; - } - - public Share documentDate(LocalDate documentDate) { - this.documentDate = documentDate; - return this; - } - - public void setDocumentDate(LocalDate documentDate) { - this.documentDate = documentDate; - } - - public LocalDate getValueDate() { - return valueDate; - } - - public Share valueDate(LocalDate valueDate) { - this.valueDate = valueDate; - return this; - } - - public void setValueDate(LocalDate valueDate) { - this.valueDate = valueDate; - } - - public ShareAction getAction() { - return action; - } - - public Share action(ShareAction action) { - this.action = action; - return this; - } - - public void setAction(ShareAction action) { - this.action = action; - } - - public Integer getQuantity() { - return quantity; - } - - public Share quantity(Integer quantity) { - this.quantity = quantity; - return this; - } - - public void setQuantity(Integer quantity) { - this.quantity = quantity; - } - - public String getRemark() { - return remark; - } - - public Share remark(String remark) { - this.remark = remark; - return this; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Membership getMembership() { - return membership; - } - - public Share membership(Membership membership) { - this.membership = membership; - return this; - } - - public void setMembership(Membership membership) { - this.membership = membership; - } - // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Share share = (Share) o; - if (share.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), share.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "Share{" + - "id=" + getId() + - ", documentDate='" + getDocumentDate() + "'" + - ", valueDate='" + getValueDate() + "'" + - ", action='" + getAction() + "'" + - ", quantity=" + getQuantity() + - ", remark='" + getRemark() + "'" + - "}"; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/User.java b/src/main/java/org/hostsharing/hsadminng/domain/User.java deleted file mode 100644 index d16b9f5b..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/User.java +++ /dev/null @@ -1,240 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import org.hostsharing.hsadminng.config.Constants; -import org.hostsharing.hsadminng.service.dto.FluentBuilder; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import org.apache.commons.lang3.StringUtils; -import org.hibernate.annotations.BatchSize; - -import java.io.Serializable; -import java.time.Instant; -import java.util.HashSet; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; - -import javax.persistence.*; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; - -/** - * A user. - */ -@Entity -@Table(name = "jhi_user") - -public class User extends AbstractAuditingEntity implements FluentBuilder, Serializable { - - private static final long serialVersionUID = 1L; - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") - @SequenceGenerator(name = "sequenceGenerator") - private Long id; - - @NotNull - @Pattern(regexp = Constants.LOGIN_REGEX) - @Size(min = 1, max = 50) - @Column(length = 50, unique = true, nullable = false) - private String login; - - @JsonIgnore - @NotNull - @Size(min = 60, max = 60) - @Column(name = "password_hash", length = 60, nullable = false) - private String password; - - @Size(max = 50) - @Column(name = "first_name", length = 50) - private String firstName; - - @Size(max = 50) - @Column(name = "last_name", length = 50) - private String lastName; - - @Email - @Size(min = 5, max = 254) - @Column(length = 254, unique = true) - private String email; - - @NotNull - @Column(nullable = false) - private boolean activated = false; - - @Size(min = 2, max = 6) - @Column(name = "lang_key", length = 6) - private String langKey; - - @Size(max = 256) - @Column(name = "image_url", length = 256) - private String imageUrl; - - @Size(max = 20) - @Column(name = "activation_key", length = 20) - @JsonIgnore - private String activationKey; - - @Size(max = 20) - @Column(name = "reset_key", length = 20) - @JsonIgnore - private String resetKey; - - @Column(name = "reset_date") - private Instant resetDate = null; - - @JsonIgnore - @ManyToMany - @JoinTable( - name = "jhi_user_authority", - joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id") }, - inverseJoinColumns = { @JoinColumn(name = "authority_name", referencedColumnName = "name") }) - @BatchSize(size = 20) - private Set authorities = new HashSet<>(); - - public Long getId() { - return id; - } - - public User id(final long id) { - this.id = id; - return this; - } - - public void setId(Long id) { - this.id = id; - } - - public String getLogin() { - return login; - } - - // Lowercase the login before saving it in database - public void setLogin(String login) { - this.login = StringUtils.lowerCase(login, Locale.ENGLISH); - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = 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 getImageUrl() { - return imageUrl; - } - - public void setImageUrl(String imageUrl) { - this.imageUrl = imageUrl; - } - - public boolean getActivated() { - return activated; - } - - public void setActivated(boolean activated) { - this.activated = activated; - } - - public String getActivationKey() { - return activationKey; - } - - public void setActivationKey(String activationKey) { - this.activationKey = activationKey; - } - - public String getResetKey() { - return resetKey; - } - - public void setResetKey(String resetKey) { - this.resetKey = resetKey; - } - - public Instant getResetDate() { - return resetDate; - } - - public void setResetDate(Instant resetDate) { - this.resetDate = resetDate; - } - - public String getLangKey() { - return langKey; - } - - public void setLangKey(String langKey) { - this.langKey = langKey; - } - - public Set getAuthorities() { - return authorities; - } - - public void setAuthorities(Set authorities) { - this.authorities = authorities; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - User user = (User) o; - return !(user.getId() == null || getId() == null) && Objects.equals(getId(), user.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "User{" + - "login='" + login + '\'' + - ", firstName='" + firstName + '\'' + - ", lastName='" + lastName + '\'' + - ", email='" + email + '\'' + - ", imageUrl='" + imageUrl + '\'' + - ", activated='" + activated + '\'' + - ", langKey='" + langKey + '\'' + - ", activationKey='" + activationKey + '\'' + - "}"; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/UserRoleAssignment.java b/src/main/java/org/hostsharing/hsadminng/domain/UserRoleAssignment.java deleted file mode 100644 index 536feb26..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/UserRoleAssignment.java +++ /dev/null @@ -1,214 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain; - -import static org.hostsharing.hsadminng.service.util.ReflectionUtil.of; - -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.*; -import org.hostsharing.hsadminng.service.accessfilter.Role.Admin; -import org.hostsharing.hsadminng.service.accessfilter.Role.Supporter; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.TreeNode; - -import org.springframework.boot.jackson.JsonComponent; -import org.springframework.context.ApplicationContext; - -import java.lang.reflect.Field; -import java.util.Objects; - -import javax.persistence.*; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * A UserRoleAssignment. - */ -@Entity -@Table(name = "user_role_assignment") -@EntityTypeId(UserRoleAssignment.ENTITY_TYPE_ID) -@JsonAutoDetect( - fieldVisibility = JsonAutoDetect.Visibility.ANY, - getterVisibility = JsonAutoDetect.Visibility.NONE, - setterVisibility = JsonAutoDetect.Visibility.NONE) -public class UserRoleAssignment implements AccessMappings { - - private static final long serialVersionUID = 1L; - - private static final String USER_FIELD_NAME = "user"; - - public static final String ENTITY_TYPE_ID = "rights.UserRoleAssignment"; - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") - @SequenceGenerator(name = "sequenceGenerator") - @SelfId(resolver = UserRoleAssignmentService.class) - @AccessFor(read = Supporter.class) - private Long id; - - @NotNull - @Size(max = 32) - @Column(name = "entity_type_id", length = 32, nullable = false) - @AccessFor(init = Admin.class, update = Admin.class, read = Supporter.class) - private String entityTypeId; - - @NotNull - @Column(name = "entity_object_id", nullable = false) - @AccessFor(init = Admin.class, update = Admin.class, read = Supporter.class) - private Long entityObjectId; - - @NotNull - @Column(name = "assigned_role", nullable = false) - @AccessFor(init = Admin.class, update = Admin.class, read = Supporter.class) - private String assignedRole; - - @ManyToOne - @JsonIgnoreProperties("requireds") - @AccessFor(init = Admin.class, update = Admin.class, read = Supporter.class) - private User user; - - // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove - - public Long getId() { - return id; - } - - public UserRoleAssignment id(final long id) { - this.id = id; - return this; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEntityTypeId() { - return entityTypeId; - } - - public UserRoleAssignment entityTypeId(String entityTypeId) { - this.entityTypeId = entityTypeId; - return this; - } - - public void setEntityTypeId(String entityTypeId) { - this.entityTypeId = entityTypeId; - } - - public Long getEntityObjectId() { - return entityObjectId; - } - - public UserRoleAssignment entityObjectId(Long entityObjectId) { - this.entityObjectId = entityObjectId; - return this; - } - - public void setEntityObjectId(Long entityObjectId) { - this.entityObjectId = entityObjectId; - } - - public Role getAssignedRole() { - return assignedRole != null ? Role.of(assignedRole) : null; - } - - public UserRoleAssignment assignedRole(Role assignedRole) { - this.assignedRole = of(assignedRole, Role::name); - return this; - } - - public void setAssignedRole(Role assignedRole) { - this.assignedRole = of(assignedRole, Role::name); - } - - public User getUser() { - return user; - } - - public UserRoleAssignment user(User user) { - this.user = user; - return this; - } - - public void setUser(User user) { - this.user = user; - } - - // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - UserRoleAssignment userRoleAssignment = (UserRoleAssignment) o; - if (userRoleAssignment.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), userRoleAssignment.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "UserRoleAssignment{" + - "id=" + getId() + - ", entityTypeId='" + entityTypeId + "'" + - ", entityObjectId=" + entityObjectId + - ", assignedRole='" + assignedRole + "'" + - "}"; - } - - @JsonComponent - public static class UserRoleAssignmentJsonSerializer extends JsonSerializerWithAccessFilter { - - public UserRoleAssignmentJsonSerializer( - final ApplicationContext ctx, - final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - - @Override - protected JSonFieldWriter jsonFieldWriter(final Field field) { - if (USER_FIELD_NAME.equals(field.getName())) { - return (final UserRoleAssignment dto, final JsonGenerator jsonGenerator) -> jsonGenerator - .writeNumberField(USER_FIELD_NAME, dto.getUser().getId()); - } - return super.jsonFieldWriter(field); - } - } - - @JsonComponent - public static class UserRoleAssignmentJsonDeserializer extends JsonDeserializerWithAccessFilter { - - private final UserRepository userRepository; - - public UserRoleAssignmentJsonDeserializer( - final UserRepository userRepository, - final ApplicationContext ctx, - final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - this.userRepository = userRepository; - } - - @Override - protected JSonFieldReader jsonFieldReader(final TreeNode treeNode, final Field field) { - if ("user".equals(field.getName())) { - return (final UserRoleAssignment target) -> target - .setUser(userRepository.getOne(getSubNode(treeNode, "id").asLong())); - } - - return super.jsonFieldReader(treeNode, field); - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/enumeration/AssetAction.java b/src/main/java/org/hostsharing/hsadminng/domain/enumeration/AssetAction.java deleted file mode 100644 index c60ee96a..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/enumeration/AssetAction.java +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain.enumeration; - -/** - * The AssetAction enumeration. - */ -public enum AssetAction { - PAYMENT, - HANDOVER, - ADOPTION, - LOSS, - CLEARING, - PAYBACK -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/enumeration/CustomerKind.java b/src/main/java/org/hostsharing/hsadminng/domain/enumeration/CustomerKind.java deleted file mode 100644 index 7e301271..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/enumeration/CustomerKind.java +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain.enumeration; - -/** - * The CustomerKind enumeration. - */ -public enum CustomerKind { - NATURAL, - LEGAL -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/enumeration/ShareAction.java b/src/main/java/org/hostsharing/hsadminng/domain/enumeration/ShareAction.java deleted file mode 100644 index c8ce2108..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/enumeration/ShareAction.java +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain.enumeration; - -/** - * The ShareAction enumeration. - */ -public enum ShareAction { - SUBSCRIPTION, - CANCELLATION -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/enumeration/VatRegion.java b/src/main/java/org/hostsharing/hsadminng/domain/enumeration/VatRegion.java deleted file mode 100644 index 067cd04e..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/enumeration/VatRegion.java +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.domain.enumeration; - -/** - * The VatRegion enumeration. - */ -public enum VatRegion { - DOMESTIC, - EU, - OTHER -} diff --git a/src/main/java/org/hostsharing/hsadminng/domain/package-info.java b/src/main/java/org/hostsharing/hsadminng/domain/package-info.java deleted file mode 100644 index d4d3e75f..00000000 --- a/src/main/java/org/hostsharing/hsadminng/domain/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * JPA domain objects. - */ -package org.hostsharing.hsadminng.domain; diff --git a/src/main/java/org/hostsharing/hsadminng/liquibase/ReplaceCustomChange.java b/src/main/java/org/hostsharing/hsadminng/liquibase/ReplaceCustomChange.java deleted file mode 100644 index f358d125..00000000 --- a/src/main/java/org/hostsharing/hsadminng/liquibase/ReplaceCustomChange.java +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.liquibase; - -import static com.google.common.base.Verify.verify; - -import liquibase.change.custom.CustomTaskChange; -import liquibase.database.Database; -import liquibase.database.jvm.JdbcConnection; -import liquibase.exception.CustomChangeException; -import liquibase.exception.DatabaseException; -import liquibase.exception.SetupException; -import liquibase.exception.ValidationErrors; -import liquibase.resource.ResourceAccessor; - -import java.sql.SQLException; -import java.sql.Statement; - -/** - * Used in Liquibase for database-independent search and replace with special characters. - */ -public class ReplaceCustomChange implements CustomTaskChange { - - private String tableName; - private String columnNames; - private String searchFor; - private String replaceWith; - - @Override - public void execute(final Database database) throws CustomChangeException { - final JdbcConnection conn = (JdbcConnection) database.getConnection(); - final boolean isH2 = "H2".equals(database.getDatabaseProductName()); - try { - verify(!conn.getAutoCommit(), "assuming auto commit to be off in Liquibase changeset"); - final Statement statement = conn.createStatement(); - for (String columnName : columnNames.split(",")) { - final String sql = "UPDATE " + tableName + " SET " + columnName + "= replace(" + columnName + ", '" + searchFor - + "', " + - (isH2 ? "STRINGDECODE('" + replaceWith + "')" : "E'" + replaceWith + "'") + ")"; - statement.executeUpdate(sql); - } - } catch (DatabaseException | SQLException e) { - throw new CustomChangeException("cannot perform search&replace", e); - } - } - - @Override - public String getConfirmationMessage() { - return "in table " + tableName + " / columns " + columnNames + ": replaced all '" + searchFor + "' to '" + replaceWith - + "'"; - } - - @Override - public void setUp() throws SetupException { - - } - - @Override - public void setFileOpener(final ResourceAccessor resourceAccessor) { - - } - - @Override - public ValidationErrors validate(final Database database) { - return null; - } - - // public String getTableName() { - // return tableName; - // } - - public void setTableName(final String tableName) { - this.tableName = tableName; - } - - // public String getColumnNames() { - // return columnNames; - // } - - public void setColumnNames(final String columns) { - this.columnNames = columns; - } - - // public String getSearchFor() { - // return searchFor; - // } - - public void setSearchFor(final String searchFor) { - this.searchFor = searchFor; - } - - // public String getReplaceWith() { - // return replaceWith; - // } - - public void setReplaceWith(final String replaceWith) { - this.replaceWith = replaceWith; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/AssetRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/AssetRepository.java deleted file mode 100644 index cb8f92c8..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/AssetRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.domain.Asset; - -import org.springframework.data.jpa.repository.*; -import org.springframework.stereotype.Repository; - -/** - * Spring Data repository for the Asset entity. - */ -@SuppressWarnings("unused") -@Repository -public interface AssetRepository extends JpaRepository, JpaSpecificationExecutor { - -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/AuthorityRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/AuthorityRepository.java deleted file mode 100644 index 2d7a8c20..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/AuthorityRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.domain.Authority; - -import org.springframework.data.jpa.repository.JpaRepository; - -/** - * Spring Data JPA repository for the Authority entity. - */ -public interface AuthorityRepository extends JpaRepository { -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/CustomAuditEventRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/CustomAuditEventRepository.java deleted file mode 100644 index 54eb2165..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/CustomAuditEventRepository.java +++ /dev/null @@ -1,94 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.config.Constants; -import org.hostsharing.hsadminng.config.audit.AuditEventConverter; -import org.hostsharing.hsadminng.domain.PersistentAuditEvent; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.audit.AuditEvent; -import org.springframework.boot.actuate.audit.AuditEventRepository; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; -import java.util.*; - -/** - * An implementation of Spring Boot's AuditEventRepository. - */ -@Repository -public class CustomAuditEventRepository implements AuditEventRepository { - - private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE"; - - /** - * Should be the same as in Liquibase migration. - */ - protected static final int EVENT_DATA_COLUMN_MAX_LENGTH = 255; - - private final PersistenceAuditEventRepository persistenceAuditEventRepository; - - private final AuditEventConverter auditEventConverter; - - private final Logger log = LoggerFactory.getLogger(getClass()); - - public CustomAuditEventRepository( - PersistenceAuditEventRepository persistenceAuditEventRepository, - AuditEventConverter auditEventConverter) { - - this.persistenceAuditEventRepository = persistenceAuditEventRepository; - this.auditEventConverter = auditEventConverter; - } - - @Override - public List find(String principal, Instant after, String type) { - Iterable persistentAuditEvents = persistenceAuditEventRepository - .findByPrincipalAndAuditEventDateAfterAndAuditEventType(principal, after, type); - return auditEventConverter.convertToAuditEvent(persistentAuditEvents); - } - - @Override - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void add(AuditEvent event) { - if (!AUTHORIZATION_FAILURE.equals(event.getType()) && - !Constants.ANONYMOUS_USER.equals(event.getPrincipal())) { - - PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent(); - persistentAuditEvent.setPrincipal(event.getPrincipal()); - persistentAuditEvent.setAuditEventType(event.getType()); - persistentAuditEvent.setAuditEventDate(event.getTimestamp()); - Map eventData = auditEventConverter.convertDataToStrings(event.getData()); - persistentAuditEvent.setData(truncate(eventData)); - persistenceAuditEventRepository.save(persistentAuditEvent); - } - } - - /** - * Truncate event data that might exceed column length. - */ - private Map truncate(Map data) { - Map results = new HashMap<>(); - - if (data != null) { - for (Map.Entry entry : data.entrySet()) { - String value = entry.getValue(); - if (value != null) { - int length = value.length(); - if (length > EVENT_DATA_COLUMN_MAX_LENGTH) { - value = value.substring(0, EVENT_DATA_COLUMN_MAX_LENGTH); - log.warn( - "Event data for {} too long ({}) has been truncated to {}. Consider increasing column width.", - entry.getKey(), - length, - EVENT_DATA_COLUMN_MAX_LENGTH); - } - } - results.put(entry.getKey(), value); - } - } - return results; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/CustomerRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/CustomerRepository.java deleted file mode 100644 index 9c434cb2..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/CustomerRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.domain.Customer; - -import org.springframework.data.jpa.repository.*; -import org.springframework.stereotype.Repository; - -/** - * Spring Data repository for the Customer entity. - */ -@SuppressWarnings("unused") -@Repository -public interface CustomerRepository extends JpaRepository, JpaSpecificationExecutor { - -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/MembershipRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/MembershipRepository.java deleted file mode 100644 index 291deca7..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/MembershipRepository.java +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.domain.Membership; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -/** - * Spring Data repository for the Membership entity. - */ -@SuppressWarnings("unused") -@Repository -public interface MembershipRepository extends JpaRepository, JpaSpecificationExecutor { - - @Query("SELECT CASE WHEN COUNT(m)> 0 THEN TRUE ELSE FALSE END " + - " FROM Membership m WHERE m.customer.id=:customerId AND m.memberUntilDate IS NULL") - boolean hasUncancelledMembershipForCustomer(@Param("customerId") final long customerId); -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/PersistenceAuditEventRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/PersistenceAuditEventRepository.java deleted file mode 100644 index 18e367cc..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/PersistenceAuditEventRepository.java +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.domain.PersistentAuditEvent; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.time.Instant; -import java.util.List; - -/** - * Spring Data JPA repository for the PersistentAuditEvent entity. - */ -public interface PersistenceAuditEventRepository extends JpaRepository { - - List findByPrincipal(String principal); - - List findByAuditEventDateAfter(Instant after); - - List findByPrincipalAndAuditEventDateAfter(String principal, Instant after); - - List findByPrincipalAndAuditEventDateAfterAndAuditEventType( - String principal, - Instant after, - String type); - - Page findAllByAuditEventDateBetween(Instant fromDate, Instant toDate, Pageable pageable); -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/SepaMandateRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/SepaMandateRepository.java deleted file mode 100644 index d1ad0459..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/SepaMandateRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.domain.SepaMandate; - -import org.springframework.data.jpa.repository.*; -import org.springframework.stereotype.Repository; - -/** - * Spring Data repository for the SepaMandate entity. - */ -@SuppressWarnings("unused") -@Repository -public interface SepaMandateRepository extends JpaRepository, JpaSpecificationExecutor { - -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/ShareRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/ShareRepository.java deleted file mode 100644 index 12cd086e..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/ShareRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.domain.Share; - -import org.springframework.data.jpa.repository.*; -import org.springframework.stereotype.Repository; - -/** - * Spring Data repository for the Share entity. - */ -@SuppressWarnings("unused") -@Repository -public interface ShareRepository extends JpaRepository, JpaSpecificationExecutor { - -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/UserRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/UserRepository.java deleted file mode 100644 index f91ff561..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/UserRepository.java +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.domain.User; - -import org.springframework.cache.annotation.Cacheable; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.EntityGraph; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.time.Instant; -import java.util.List; -import java.util.Optional; - -/** - * Spring Data JPA repository for the User entity. - */ -@Repository -public interface UserRepository extends JpaRepository { - - String USERS_BY_LOGIN_CACHE = "usersByLogin"; - - String USERS_BY_EMAIL_CACHE = "usersByEmail"; - - Optional findOneByActivationKey(String activationKey); - - List findAllByActivatedIsFalseAndCreatedDateBefore(Instant dateTime); - - Optional findOneByResetKey(String resetKey); - - Optional findOneByEmailIgnoreCase(String email); - - Optional findOneByLogin(String login); - - @EntityGraph(attributePaths = "authorities") - Optional findOneWithAuthoritiesById(Long id); - - @EntityGraph(attributePaths = "authorities") - @Cacheable(cacheNames = USERS_BY_LOGIN_CACHE) - Optional findOneWithAuthoritiesByLogin(String login); - - @EntityGraph(attributePaths = "authorities") - @Cacheable(cacheNames = USERS_BY_EMAIL_CACHE) - Optional findOneWithAuthoritiesByEmail(String email); - - Page findAllByLoginNot(Pageable pageable, String login); -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/UserRoleAssignmentRepository.java b/src/main/java/org/hostsharing/hsadminng/repository/UserRoleAssignmentRepository.java deleted file mode 100644 index 0fdf64c9..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/UserRoleAssignmentRepository.java +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import org.hostsharing.hsadminng.domain.UserRoleAssignment; - -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.data.jpa.repository.*; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import java.util.List; - -/** - * Spring Data repository for the UserRoleAssignment entity. - */ -@SuppressWarnings("unused") -@Repository -public interface UserRoleAssignmentRepository - extends JpaRepository, JpaSpecificationExecutor { - - String CURRENT_USER_ROLE_ASSIGNMENTS_CACHE = "currentUserRoleAssignments"; - - // TODO mhoennig: optimize with query by id - @Cacheable(CURRENT_USER_ROLE_ASSIGNMENTS_CACHE) - @Query("select user_role_assignment from UserRoleAssignment user_role_assignment where user_role_assignment.user.login = :login") - List findByLogin(@Param("login") final String login); - - @CacheEvict(value = CURRENT_USER_ROLE_ASSIGNMENTS_CACHE, allEntries = true) - default void evictCache() { - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/repository/package-info.java b/src/main/java/org/hostsharing/hsadminng/repository/package-info.java deleted file mode 100644 index 12710c57..00000000 --- a/src/main/java/org/hostsharing/hsadminng/repository/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Spring Data JPA repositories. - */ -package org.hostsharing.hsadminng.repository; diff --git a/src/main/java/org/hostsharing/hsadminng/security/AuthoritiesConstants.java b/src/main/java/org/hostsharing/hsadminng/security/AuthoritiesConstants.java deleted file mode 100644 index 6df501ec..00000000 --- a/src/main/java/org/hostsharing/hsadminng/security/AuthoritiesConstants.java +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security; - -/** - * Constants for Spring Security authorities. - */ -public final class AuthoritiesConstants { - - public static final String HOSTMASTER = "ROLE_HOSTMASTER"; - - public static final String ADMIN = "ROLE_ADMIN"; - - public static final String SUPPORTER = "ROLE_SUPPORTER"; - - public static final String USER = "ROLE_USER"; - - public static final String ANONYMOUS = "ROLE_ANONYMOUS"; - - private AuthoritiesConstants() { - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/security/DomainUserDetailsService.java b/src/main/java/org/hostsharing/hsadminng/security/DomainUserDetailsService.java deleted file mode 100644 index 570f0449..00000000 --- a/src/main/java/org/hostsharing/hsadminng/security/DomainUserDetailsService.java +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security; - -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.repository.UserRepository; - -import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Authenticate a user from the database. - */ -@Component("userDetailsService") -public class DomainUserDetailsService implements UserDetailsService { - - private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class); - - private final UserRepository userRepository; - - public DomainUserDetailsService(UserRepository userRepository) { - this.userRepository = userRepository; - } - - @Override - @Transactional - public UserDetails loadUserByUsername(final String login) { - log.debug("Authenticating {}", login); - - if (new EmailValidator().isValid(login, null)) { - return userRepository.findOneWithAuthoritiesByEmail(login) - .map(user -> createSpringSecurityUser(login, user)) - .orElseThrow( - () -> new UsernameNotFoundException("User with email " + login + " was not found in the database")); - } - - String lowercaseLogin = login.toLowerCase(Locale.ENGLISH); - return userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin) - .map(user -> createSpringSecurityUser(lowercaseLogin, user)) - .orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database")); - - } - - private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) { - if (!user.getActivated()) { - throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated"); - } - List grantedAuthorities = user.getAuthorities() - .stream() - .map(authority -> new SimpleGrantedAuthority(authority.getName())) - .collect(Collectors.toList()); - return new org.springframework.security.core.userdetails.User( - user.getLogin(), - user.getPassword(), - grantedAuthorities); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/security/SecurityUtils.java b/src/main/java/org/hostsharing/hsadminng/security/SecurityUtils.java deleted file mode 100644 index 1096009e..00000000 --- a/src/main/java/org/hostsharing/hsadminng/security/SecurityUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; - -import java.util.Optional; - -/** - * Utility class for Spring Security. - */ -public final class SecurityUtils { - - private static final Logger log = LoggerFactory.getLogger(SecurityUtils.class); - - private SecurityUtils() { - } - - /** - * Get the login of the current user. - * - * @return the login of the current user - */ - public static Optional getCurrentUserLogin() { - SecurityContext securityContext = SecurityContextHolder.getContext(); - return Optional.ofNullable(securityContext.getAuthentication()) - .map(authentication -> { - if (authentication.getPrincipal() instanceof UserDetails) { - UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal(); - return springSecurityUser.getUsername(); - } else if (authentication.getPrincipal() instanceof String) { - return (String) authentication.getPrincipal(); - } - return null; - }); - } - - /** - * Get the JWT of the current user. - * - * @return the JWT of the current user - */ - public static Optional getCurrentUserJWT() { - SecurityContext securityContext = SecurityContextHolder.getContext(); - return Optional.ofNullable(securityContext.getAuthentication()) - .filter(authentication -> authentication.getCredentials() instanceof String) - .map(authentication -> (String) authentication.getCredentials()); - } - - /** - * Check if a user is authenticated. - * - * @return true if the user is authenticated, false otherwise - */ - public static boolean isAuthenticated() { - SecurityContext securityContext = SecurityContextHolder.getContext(); - return Optional.ofNullable(securityContext.getAuthentication()) - .map( - authentication -> authentication.getAuthorities() - .stream() - .noneMatch( - grantedAuthority -> grantedAuthority.getAuthority() - .equals(AuthoritiesConstants.ANONYMOUS))) - .orElse(false); - } - - /** - * If the current user has a specific authority (security role). - *

- * The name of this method comes from the isUserInRole() method in the Servlet API - * - * @param authority the authority to check - * @return true if the current user has the authority, false otherwise - */ - public static boolean isCurrentUserInRole(String authority) { - SecurityContext securityContext = SecurityContextHolder.getContext(); - final Boolean isCurrentUserInRole = Optional.ofNullable(securityContext.getAuthentication()) - .map( - authentication -> authentication.getAuthorities() - .stream() - .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority))) - .orElse(false); - return isCurrentUserInRole; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/security/SpringSecurityAuditorAware.java b/src/main/java/org/hostsharing/hsadminng/security/SpringSecurityAuditorAware.java deleted file mode 100644 index f187f01e..00000000 --- a/src/main/java/org/hostsharing/hsadminng/security/SpringSecurityAuditorAware.java +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security; - -import org.hostsharing.hsadminng.config.Constants; - -import org.springframework.data.domain.AuditorAware; -import org.springframework.stereotype.Component; - -import java.util.Optional; - -/** - * Implementation of AuditorAware based on Spring Security. - */ -@Component -public class SpringSecurityAuditorAware implements AuditorAware { - - @Override - public Optional getCurrentAuditor() { - return Optional.of(SecurityUtils.getCurrentUserLogin().orElse(Constants.SYSTEM_ACCOUNT)); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/security/UserNotActivatedException.java b/src/main/java/org/hostsharing/hsadminng/security/UserNotActivatedException.java deleted file mode 100644 index 0a94d4e3..00000000 --- a/src/main/java/org/hostsharing/hsadminng/security/UserNotActivatedException.java +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security; - -import org.springframework.security.core.AuthenticationException; - -/** - * This exception is thrown in case of a not activated user trying to authenticate. - */ -public class UserNotActivatedException extends AuthenticationException { - - private static final long serialVersionUID = 1L; - - public UserNotActivatedException(String message) { - super(message); - } - - public UserNotActivatedException(String message, Throwable t) { - super(message, t); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/security/jwt/JWTConfigurer.java b/src/main/java/org/hostsharing/hsadminng/security/jwt/JWTConfigurer.java deleted file mode 100644 index 5e4af8c8..00000000 --- a/src/main/java/org/hostsharing/hsadminng/security/jwt/JWTConfigurer.java +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security.jwt; - -import org.springframework.security.config.annotation.SecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.web.DefaultSecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -public class JWTConfigurer extends SecurityConfigurerAdapter { - - private TokenProvider tokenProvider; - - public JWTConfigurer(TokenProvider tokenProvider) { - this.tokenProvider = tokenProvider; - } - - @Override - public void configure(HttpSecurity http) throws Exception { - JWTFilter customFilter = new JWTFilter(tokenProvider); - http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/security/jwt/JWTFilter.java b/src/main/java/org/hostsharing/hsadminng/security/jwt/JWTFilter.java deleted file mode 100644 index 1eed81b8..00000000 --- a/src/main/java/org/hostsharing/hsadminng/security/jwt/JWTFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security.jwt; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.util.StringUtils; -import org.springframework.web.filter.GenericFilterBean; - -import java.io.IOException; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; - -/** - * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is - * found. - */ -public class JWTFilter extends GenericFilterBean { - - public static final String AUTHORIZATION_HEADER = "Authorization"; - - private TokenProvider tokenProvider; - - public JWTFilter(TokenProvider tokenProvider) { - this.tokenProvider = tokenProvider; - } - - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) - throws IOException, ServletException { - HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; - String jwt = resolveToken(httpServletRequest); - if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) { - Authentication authentication = this.tokenProvider.getAuthentication(jwt); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - filterChain.doFilter(servletRequest, servletResponse); - } - - private String resolveToken(HttpServletRequest request) { - String bearerToken = request.getHeader(AUTHORIZATION_HEADER); - if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { - return bearerToken.substring(7); - } - return null; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/security/jwt/TokenProvider.java b/src/main/java/org/hostsharing/hsadminng/security/jwt/TokenProvider.java deleted file mode 100644 index ff315141..00000000 --- a/src/main/java/org/hostsharing/hsadminng/security/jwt/TokenProvider.java +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security.jwt; - -import io.github.jhipster.config.JHipsterProperties; -import io.jsonwebtoken.*; -import io.jsonwebtoken.io.Decoders; -import io.jsonwebtoken.security.Keys; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -import java.nio.charset.StandardCharsets; -import java.security.Key; -import java.util.*; -import java.util.stream.Collectors; - -import javax.annotation.PostConstruct; - -@Component -public class TokenProvider { - - private final Logger log = LoggerFactory.getLogger(TokenProvider.class); - - private static final String AUTHORITIES_KEY = "auth"; - - private Key key; - - private long tokenValidityInMilliseconds; - - private long tokenValidityInMillisecondsForRememberMe; - - private final JHipsterProperties jHipsterProperties; - - public TokenProvider(JHipsterProperties jHipsterProperties) { - this.jHipsterProperties = jHipsterProperties; - } - - @PostConstruct - public void init() { - byte[] keyBytes; - String secret = jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret(); - if (!StringUtils.isEmpty(secret)) { - log.warn( - "Warning: the JWT key used is not Base64-encoded. " + - "We recommend using the `jhipster.security.authentication.jwt.base64-secret` key for optimum security."); - keyBytes = secret.getBytes(StandardCharsets.UTF_8); - } else { - log.debug("Using a Base64-encoded JWT secret key"); - keyBytes = Decoders.BASE64.decode(jHipsterProperties.getSecurity().getAuthentication().getJwt().getBase64Secret()); - } - this.key = Keys.hmacShaKeyFor(keyBytes); - this.tokenValidityInMilliseconds = 1000 - * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds(); - this.tokenValidityInMillisecondsForRememberMe = 1000 * jHipsterProperties.getSecurity() - .getAuthentication() - .getJwt() - .getTokenValidityInSecondsForRememberMe(); - } - - public String createToken(Authentication authentication, boolean rememberMe) { - String authorities = authentication.getAuthorities() - .stream() - .map(GrantedAuthority::getAuthority) - .collect(Collectors.joining(",")); - - long now = (new Date()).getTime(); - Date validity; - if (rememberMe) { - validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe); - } else { - validity = new Date(now + this.tokenValidityInMilliseconds); - } - - return Jwts.builder() - .setSubject(authentication.getName()) - .claim(AUTHORITIES_KEY, authorities) - .signWith(key, SignatureAlgorithm.HS512) - .setExpiration(validity) - .compact(); - } - - public Authentication getAuthentication(String token) { - Claims claims = Jwts.parser() - .setSigningKey(key) - .parseClaimsJws(token) - .getBody(); - - Collection authorities = Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) - .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList()); - - User principal = new User(claims.getSubject(), "", authorities); - - return new UsernamePasswordAuthenticationToken(principal, token, authorities); - } - - public boolean validateToken(String authToken) { - try { - Jwts.parser().setSigningKey(key).parseClaimsJws(authToken); - return true; - } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { - log.info("Invalid JWT signature."); - log.trace("Invalid JWT signature trace: {}", e); - } catch (ExpiredJwtException e) { - log.info("Expired JWT token."); - log.trace("Expired JWT token trace: {}", e); - } catch (UnsupportedJwtException e) { - log.info("Unsupported JWT token."); - log.trace("Unsupported JWT token trace: {}", e); - } catch (IllegalArgumentException e) { - log.info("JWT token compact of handler are invalid."); - log.trace("JWT token compact of handler are invalid trace: {}", e); - } - return false; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/security/package-info.java b/src/main/java/org/hostsharing/hsadminng/security/package-info.java deleted file mode 100644 index 9df55cbb..00000000 --- a/src/main/java/org/hostsharing/hsadminng/security/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Spring Security configuration. - */ -package org.hostsharing.hsadminng.security; diff --git a/src/main/java/org/hostsharing/hsadminng/service/AssetQueryService.java b/src/main/java/org/hostsharing/hsadminng/service/AssetQueryService.java deleted file mode 100644 index 3e869f24..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/AssetQueryService.java +++ /dev/null @@ -1,120 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.*; -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.repository.AssetRepository; -import org.hostsharing.hsadminng.service.dto.AssetCriteria; -import org.hostsharing.hsadminng.service.dto.AssetDTO; -import org.hostsharing.hsadminng.service.mapper.AssetMapper; - -import io.github.jhipster.service.QueryService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -import javax.persistence.criteria.JoinType; - -/** - * Service for executing complex queries for Asset entities in the database. - * The main input is a {@link AssetCriteria} which gets converted to {@link Specification}, - * in a way that all the filters must apply. - * It returns a {@link List} of {@link AssetDTO} or a {@link Page} of {@link AssetDTO} which fulfills the criteria. - */ -@Service -@Transactional(readOnly = true) -public class AssetQueryService extends QueryService { - - private final Logger log = LoggerFactory.getLogger(AssetQueryService.class); - - private final AssetRepository assetRepository; - - private final AssetMapper assetMapper; - - public AssetQueryService(AssetRepository assetRepository, AssetMapper assetMapper) { - this.assetRepository = assetRepository; - this.assetMapper = assetMapper; - } - - /** - * Return a {@link List} of {@link AssetDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public List findByCriteria(AssetCriteria criteria) { - log.debug("find by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return assetMapper.toDto(assetRepository.findAll(specification)); - } - - /** - * Return a {@link Page} of {@link AssetDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @param page The page, which should be returned. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public Page findByCriteria(AssetCriteria criteria, Pageable page) { - log.debug("find by criteria : {}, page: {}", criteria, page); - final Specification specification = createSpecification(criteria); - return assetRepository.findAll(specification, page) - .map(assetMapper::toDto); - } - - /** - * Return the number of matching entities in the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the number of matching entities. - */ - @Transactional(readOnly = true) - public long countByCriteria(AssetCriteria criteria) { - log.debug("count by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return assetRepository.count(specification); - } - - /** - * Function to convert AssetCriteria to a {@link Specification} - */ - private Specification createSpecification(AssetCriteria criteria) { - Specification specification = Specification.where(null); - if (criteria != null) { - if (criteria.getId() != null) { - specification = specification.and(buildSpecification(criteria.getId(), Asset_.id)); - } - if (criteria.getDocumentDate() != null) { - specification = specification.and(buildRangeSpecification(criteria.getDocumentDate(), Asset_.documentDate)); - } - if (criteria.getValueDate() != null) { - specification = specification.and(buildRangeSpecification(criteria.getValueDate(), Asset_.valueDate)); - } - if (criteria.getAction() != null) { - specification = specification.and(buildSpecification(criteria.getAction(), Asset_.action)); - } - if (criteria.getAmount() != null) { - specification = specification.and(buildRangeSpecification(criteria.getAmount(), Asset_.amount)); - } - if (criteria.getRemark() != null) { - specification = specification.and(buildStringSpecification(criteria.getRemark(), Asset_.remark)); - } - if (criteria.getMembershipId() != null) { - specification = specification.and( - buildSpecification( - criteria.getMembershipId(), - root -> root.join(Asset_.membership, JoinType.LEFT).get(Membership_.id))); - } - } - return specification; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/AssetService.java b/src/main/java/org/hostsharing/hsadminng/service/AssetService.java deleted file mode 100644 index f7b71120..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/AssetService.java +++ /dev/null @@ -1,100 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.repository.AssetRepository; -import org.hostsharing.hsadminng.service.dto.AssetDTO; -import org.hostsharing.hsadminng.service.mapper.AssetMapper; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; - -import javax.persistence.EntityManager; - -/** - * Service Implementation for managing Asset. - */ -@Service -@Transactional -public class AssetService implements IdToDtoResolver { - - private final Logger log = LoggerFactory.getLogger(AssetService.class); - - private final EntityManager em; - - private final AssetRepository assetRepository; - - private final AssetMapper assetMapper; - private final AssetValidator assetValidator; - - public AssetService( - final EntityManager em, - final AssetRepository assetRepository, - final AssetMapper assetMapper, - final AssetValidator assetValidator) { - this.em = em; - this.assetRepository = assetRepository; - this.assetMapper = assetMapper; - this.assetValidator = assetValidator; - } - - /** - * Save a asset. - * - * @param assetDTO the entity to save - * @return the persisted entity - */ - public AssetDTO save(AssetDTO assetDTO) { - log.debug("Request to save Asset : {}", assetDTO); - assetValidator.validate(assetDTO); - Asset asset = assetMapper.toEntity(assetDTO); - asset = assetRepository.save(asset); - em.flush(); - em.refresh(asset); - return assetMapper.toDto(asset); - } - - /** - * Get all the assets. - * - * @param pageable the pagination information - * @return the list of entities - */ - @Transactional(readOnly = true) - public Page findAll(Pageable pageable) { - log.debug("Request to get all Assets"); - return assetRepository.findAll(pageable) - .map(assetMapper::toDto); - } - - /** - * Get one asset by id. - * - * @param id the id of the entity - * @return the entity - */ - @Transactional(readOnly = true) - public Optional findOne(Long id) { - log.debug("Request to get Asset : {}", id); - return assetRepository.findById(id) - .map(assetMapper::toDto); - } - - /** - * Delete the asset by id. - * - * @param id the id of the entity - */ - public void delete(Long id) { - log.debug("Request to delete Asset : {}", id); - - throw new BadRequestAlertException("Asset transactions are immutable", Asset.ENTITY_NAME, "assetTransactionImmutable"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/AssetValidator.java b/src/main/java/org/hostsharing/hsadminng/service/AssetValidator.java deleted file mode 100644 index 1d0f9620..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/AssetValidator.java +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; -import org.hostsharing.hsadminng.service.dto.AssetDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.springframework.stereotype.Service; - -import java.math.BigDecimal; - -@Service -public class AssetValidator { - - public void validate(final AssetDTO assetDTO) { - if (assetDTO.getId() != null) { - throw new BadRequestAlertException( - "Asset transactions are immutable", - Asset.ENTITY_NAME, - "assetTransactionImmutable"); - } - - if (assetDTO.getDocumentDate().isAfter(assetDTO.getValueDate())) { - throw new BadRequestAlertException( - "Document date may not be after value date", - Asset.ENTITY_NAME, - "documentDateMayNotBeAfterValueDate"); - } - - if ((assetDTO.getAction() == AssetAction.PAYMENT) && (assetDTO.getAmount().compareTo(BigDecimal.valueOf(0)) <= 0)) { - throw new BadRequestAlertException( - "Asset payments require a positive amount", - Asset.ENTITY_NAME, - "assetPaymentsPositiveAmount"); - } - if ((assetDTO.getAction() == AssetAction.ADOPTION) && (assetDTO.getAmount().compareTo(BigDecimal.valueOf(0)) <= 0)) { - throw new BadRequestAlertException( - "Asset adoptions require a positive amount", - Asset.ENTITY_NAME, - "assetAdoptionsPositiveAmount"); - } - - if ((assetDTO.getAction() == AssetAction.PAYBACK) && (assetDTO.getAmount().compareTo(BigDecimal.valueOf(0)) >= 0)) { - throw new BadRequestAlertException( - "Asset paybacks require a negative amount", - Asset.ENTITY_NAME, - "assetPaybacksNegativeAmount"); - } - if ((assetDTO.getAction() == AssetAction.HANDOVER) && (assetDTO.getAmount().compareTo(BigDecimal.valueOf(0)) >= 0)) { - throw new BadRequestAlertException( - "Asset handovers require a negative amount", - Asset.ENTITY_NAME, - "assetHandoversNegativeAmount"); - } - if ((assetDTO.getAction() == AssetAction.LOSS) && (assetDTO.getAmount().compareTo(BigDecimal.valueOf(0)) >= 0)) { - throw new BadRequestAlertException( - "Asset losses require a negative amount", - Asset.ENTITY_NAME, - "assetLossesNegativeAmount"); - } - if ((assetDTO.getAction() == AssetAction.CLEARING) && (assetDTO.getAmount().compareTo(BigDecimal.valueOf(0)) >= 0)) { - throw new BadRequestAlertException( - "Asset clearings require a negative amount", - Asset.ENTITY_NAME, - "assetClearingsNegativeAmount"); - } - - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/AuditEventService.java b/src/main/java/org/hostsharing/hsadminng/service/AuditEventService.java deleted file mode 100644 index b41fb9c2..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/AuditEventService.java +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.config.audit.AuditEventConverter; -import org.hostsharing.hsadminng.repository.PersistenceAuditEventRepository; - -import org.springframework.boot.actuate.audit.AuditEvent; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; -import java.util.Optional; - -/** - * Service for managing audit events. - *

- * This is the default implementation to support SpringBoot Actuator AuditEventRepository - */ -@Service -@Transactional -public class AuditEventService { - - private final PersistenceAuditEventRepository persistenceAuditEventRepository; - - private final AuditEventConverter auditEventConverter; - - public AuditEventService( - PersistenceAuditEventRepository persistenceAuditEventRepository, - AuditEventConverter auditEventConverter) { - - this.persistenceAuditEventRepository = persistenceAuditEventRepository; - this.auditEventConverter = auditEventConverter; - } - - public Page findAll(Pageable pageable) { - return persistenceAuditEventRepository.findAll(pageable) - .map(auditEventConverter::convertToAuditEvent); - } - - public Page findByDates(Instant fromDate, Instant toDate, Pageable pageable) { - return persistenceAuditEventRepository.findAllByAuditEventDateBetween(fromDate, toDate, pageable) - .map(auditEventConverter::convertToAuditEvent); - } - - public Optional find(Long id) { - return Optional.ofNullable(persistenceAuditEventRepository.findById(id)) - .filter(Optional::isPresent) - .map(Optional::get) - .map(auditEventConverter::convertToAuditEvent); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/CustomerQueryService.java b/src/main/java/org/hostsharing/hsadminng/service/CustomerQueryService.java deleted file mode 100644 index b3352819..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/CustomerQueryService.java +++ /dev/null @@ -1,164 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Customer_; -import org.hostsharing.hsadminng.domain.Membership_; -import org.hostsharing.hsadminng.domain.SepaMandate_; -import org.hostsharing.hsadminng.repository.CustomerRepository; -import org.hostsharing.hsadminng.service.dto.CustomerCriteria; -import org.hostsharing.hsadminng.service.dto.CustomerDTO; -import org.hostsharing.hsadminng.service.mapper.CustomerMapper; - -import io.github.jhipster.service.QueryService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -import javax.persistence.criteria.JoinType; - -/** - * Service for executing complex queries for Customer entities in the database. - * The main input is a {@link CustomerCriteria} which gets converted to {@link Specification}, - * in a way that all the filters must apply. - * It returns a {@link List} of {@link CustomerDTO} or a {@link Page} of {@link CustomerDTO} which fulfills the criteria. - */ -@Service -@Transactional(readOnly = true) -public class CustomerQueryService extends QueryService { - - private final Logger log = LoggerFactory.getLogger(CustomerQueryService.class); - - private final CustomerRepository customerRepository; - - private final CustomerMapper customerMapper; - - public CustomerQueryService(CustomerRepository customerRepository, CustomerMapper customerMapper) { - this.customerRepository = customerRepository; - this.customerMapper = customerMapper; - } - - /** - * Return a {@link List} of {@link CustomerDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public List findByCriteria(CustomerCriteria criteria) { - log.debug("find by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return customerMapper.toDto(customerRepository.findAll(specification)); - } - - /** - * Return a {@link Page} of {@link CustomerDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @param page The page, which should be returned. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public Page findByCriteria(CustomerCriteria criteria, Pageable page) { - log.debug("find by criteria : {}, page: {}", criteria, page); - final Specification specification = createSpecification(criteria); - return customerRepository.findAll(specification, page) - .map(customerMapper::toDto); - } - - /** - * Return the number of matching entities in the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the number of matching entities. - */ - @Transactional(readOnly = true) - public long countByCriteria(CustomerCriteria criteria) { - log.debug("count by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return customerRepository.count(specification); - } - - /** - * Function to convert CustomerCriteria to a {@link Specification} - */ - private Specification createSpecification(CustomerCriteria criteria) { - Specification specification = Specification.where(null); - if (criteria != null) { - if (criteria.getId() != null) { - specification = specification.and(buildSpecification(criteria.getId(), Customer_.id)); - } - if (criteria.getReference() != null) { - specification = specification.and(buildRangeSpecification(criteria.getReference(), Customer_.reference)); - } - if (criteria.getPrefix() != null) { - specification = specification.and(buildStringSpecification(criteria.getPrefix(), Customer_.prefix)); - } - if (criteria.getName() != null) { - specification = specification.and(buildStringSpecification(criteria.getName(), Customer_.name)); - } - if (criteria.getKind() != null) { - specification = specification.and(buildSpecification(criteria.getKind(), Customer_.kind)); - } - if (criteria.getBirthDate() != null) { - specification = specification.and(buildRangeSpecification(criteria.getBirthDate(), Customer_.birthDate)); - } - if (criteria.getBirthPlace() != null) { - specification = specification.and(buildStringSpecification(criteria.getBirthPlace(), Customer_.birthPlace)); - } - if (criteria.getRegistrationCourt() != null) { - specification = specification - .and(buildStringSpecification(criteria.getRegistrationCourt(), Customer_.registrationCourt)); - } - if (criteria.getRegistrationNumber() != null) { - specification = specification - .and(buildStringSpecification(criteria.getRegistrationNumber(), Customer_.registrationNumber)); - } - if (criteria.getVatRegion() != null) { - specification = specification.and(buildSpecification(criteria.getVatRegion(), Customer_.vatRegion)); - } - if (criteria.getVatNumber() != null) { - specification = specification.and(buildStringSpecification(criteria.getVatNumber(), Customer_.vatNumber)); - } - if (criteria.getContractualSalutation() != null) { - specification = specification - .and(buildStringSpecification(criteria.getContractualSalutation(), Customer_.contractualSalutation)); - } - if (criteria.getContractualAddress() != null) { - specification = specification - .and(buildStringSpecification(criteria.getContractualAddress(), Customer_.contractualAddress)); - } - if (criteria.getBillingSalutation() != null) { - specification = specification - .and(buildStringSpecification(criteria.getBillingSalutation(), Customer_.billingSalutation)); - } - if (criteria.getBillingAddress() != null) { - specification = specification - .and(buildStringSpecification(criteria.getBillingAddress(), Customer_.billingAddress)); - } - if (criteria.getRemark() != null) { - specification = specification.and(buildStringSpecification(criteria.getRemark(), Customer_.remark)); - } - if (criteria.getMembershipId() != null) { - specification = specification.and( - buildSpecification( - criteria.getMembershipId(), - root -> root.join(Customer_.memberships, JoinType.LEFT).get(Membership_.id))); - } - if (criteria.getSepamandateId() != null) { - specification = specification.and( - buildSpecification( - criteria.getSepamandateId(), - root -> root.join(Customer_.sepamandates, JoinType.LEFT).get(SepaMandate_.id))); - } - } - return specification; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/CustomerService.java b/src/main/java/org/hostsharing/hsadminng/service/CustomerService.java deleted file mode 100644 index 48ffbc79..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/CustomerService.java +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.repository.CustomerRepository; -import org.hostsharing.hsadminng.service.dto.CustomerDTO; -import org.hostsharing.hsadminng.service.mapper.CustomerMapper; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; - -/** - * Service Implementation for managing Customer. - */ -@Service -@Transactional -public class CustomerService implements IdToDtoResolver { - - private final Logger log = LoggerFactory.getLogger(CustomerService.class); - - private final CustomerRepository customerRepository; - - private final CustomerMapper customerMapper; - - public CustomerService(CustomerRepository customerRepository, CustomerMapper customerMapper) { - this.customerRepository = customerRepository; - this.customerMapper = customerMapper; - } - - /** - * Save a customer. - * - * @param customerDTO the entity to save - * @return the persisted entity - */ - public CustomerDTO save(CustomerDTO customerDTO) { - log.debug("Request to save Customer : {}", customerDTO); - Customer customer = customerMapper.toEntity(customerDTO); - customer = customerRepository.save(customer); - return customerMapper.toDto(customer); - } - - /** - * Get all the customers. - * - * @param pageable the pagination information - * @return the list of entities - */ - @Transactional(readOnly = true) - public Page findAll(Pageable pageable) { - log.debug("Request to get all Customers"); - return customerRepository.findAll(pageable) - .map(customerMapper::toDto); - } - - /** - * Get one customer by id. - * - * @param id the id of the entity - * @return the entity - */ - @Transactional(readOnly = true) - public Optional findOne(Long id) { - log.debug("Request to get Customer : {}", id); - return customerRepository.findById(id) - .map(customerMapper::toDto); - } - - /** - * Delete the customer by id. - * - * @param id the id of the entity - */ - public void delete(Long id) { - log.debug("Request to delete Customer : {}", id); - customerRepository.deleteById(id); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/IdToDtoResolver.java b/src/main/java/org/hostsharing/hsadminng/service/IdToDtoResolver.java deleted file mode 100644 index 3b19c2ef..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/IdToDtoResolver.java +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import java.util.Optional; - -public interface IdToDtoResolver { - - Optional findOne(Long id); -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/MailService.java b/src/main/java/org/hostsharing/hsadminng/service/MailService.java deleted file mode 100644 index 14575309..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/MailService.java +++ /dev/null @@ -1,115 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.User; - -import io.github.jhipster.config.JHipsterProperties; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.MessageSource; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.MimeMessageHelper; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; -import org.thymeleaf.context.Context; -import org.thymeleaf.spring5.SpringTemplateEngine; - -import java.nio.charset.StandardCharsets; -import java.util.Locale; - -import javax.mail.internet.MimeMessage; - -/** - * Service for sending emails. - *

- * We use the @Async annotation to send emails asynchronously. - */ -@Service -public class MailService { - - private final Logger log = LoggerFactory.getLogger(MailService.class); - - private static final String USER = "user"; - - private static final String BASE_URL = "baseUrl"; - - private final JHipsterProperties jHipsterProperties; - - private final JavaMailSender javaMailSender; - - private final MessageSource messageSource; - - private final SpringTemplateEngine templateEngine; - - public MailService( - JHipsterProperties jHipsterProperties, - JavaMailSender javaMailSender, - MessageSource messageSource, - SpringTemplateEngine templateEngine) { - - this.jHipsterProperties = jHipsterProperties; - this.javaMailSender = javaMailSender; - this.messageSource = messageSource; - this.templateEngine = templateEngine; - } - - @Async - public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) { - log.debug( - "Send email[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}", - isMultipart, - isHtml, - to, - subject, - content); - - // Prepare message using a Spring helper - MimeMessage mimeMessage = javaMailSender.createMimeMessage(); - try { - MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, StandardCharsets.UTF_8.name()); - message.setTo(to); - message.setFrom(jHipsterProperties.getMail().getFrom()); - message.setSubject(subject); - message.setText(content, isHtml); - javaMailSender.send(mimeMessage); - log.debug("Sent email to User '{}'", to); - } catch (Exception e) { - if (log.isDebugEnabled()) { - log.warn("Email could not be sent to user '{}'", to, e); - } else { - log.warn("Email could not be sent to user '{}': {}", to, e.getMessage()); - } - } - } - - @Async - public void sendEmailFromTemplate(User user, String templateName, String titleKey) { - Locale locale = Locale.forLanguageTag(user.getLangKey()); - Context context = new Context(locale); - context.setVariable(USER, user); - context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl()); - String content = templateEngine.process(templateName, context); - String subject = messageSource.getMessage(titleKey, null, locale); - sendEmail(user.getEmail(), subject, content, false, true); - - } - - @Async - public void sendActivationEmail(User user) { - log.debug("Sending activation email to '{}'", user.getEmail()); - sendEmailFromTemplate(user, "mail/activationEmail", "email.activation.title"); - } - - @Async - public void sendCreationEmail(User user) { - log.debug("Sending creation email to '{}'", user.getEmail()); - sendEmailFromTemplate(user, "mail/creationEmail", "email.activation.title"); - } - - @Async - public void sendPasswordResetMail(User user) { - log.debug("Sending password reset email to '{}'", user.getEmail()); - sendEmailFromTemplate(user, "mail/passwordResetEmail", "email.reset.title"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/MembershipQueryService.java b/src/main/java/org/hostsharing/hsadminng/service/MembershipQueryService.java deleted file mode 100644 index 0c42ffd2..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/MembershipQueryService.java +++ /dev/null @@ -1,135 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.*; -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.service.dto.MembershipCriteria; -import org.hostsharing.hsadminng.service.dto.MembershipDTO; -import org.hostsharing.hsadminng.service.mapper.MembershipMapper; - -import io.github.jhipster.service.QueryService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -import javax.persistence.criteria.JoinType; - -/** - * Service for executing complex queries for Membership entities in the database. - * The main input is a {@link MembershipCriteria} which gets converted to {@link Specification}, - * in a way that all the filters must apply. - * It returns a {@link List} of {@link MembershipDTO} or a {@link Page} of {@link MembershipDTO} which fulfills the criteria. - */ -@Service -@Transactional(readOnly = true) -public class MembershipQueryService extends QueryService { - - private final Logger log = LoggerFactory.getLogger(MembershipQueryService.class); - - private final MembershipRepository membershipRepository; - - private final MembershipMapper membershipMapper; - - public MembershipQueryService(MembershipRepository membershipRepository, MembershipMapper membershipMapper) { - this.membershipRepository = membershipRepository; - this.membershipMapper = membershipMapper; - } - - /** - * Return a {@link List} of {@link MembershipDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public List findByCriteria(MembershipCriteria criteria) { - log.debug("find by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return membershipMapper.toDto(membershipRepository.findAll(specification)); - } - - /** - * Return a {@link Page} of {@link MembershipDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @param page The page, which should be returned. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public Page findByCriteria(MembershipCriteria criteria, Pageable page) { - log.debug("find by criteria : {}, page: {}", criteria, page); - final Specification specification = createSpecification(criteria); - return membershipRepository.findAll(specification, page) - .map(membershipMapper::toDto); - } - - /** - * Return the number of matching entities in the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the number of matching entities. - */ - @Transactional(readOnly = true) - public long countByCriteria(MembershipCriteria criteria) { - log.debug("count by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return membershipRepository.count(specification); - } - - /** - * Function to convert MembershipCriteria to a {@link Specification} - */ - private Specification createSpecification(MembershipCriteria criteria) { - Specification specification = Specification.where(null); - if (criteria != null) { - if (criteria.getId() != null) { - specification = specification.and(buildSpecification(criteria.getId(), Membership_.id)); - } - if (criteria.getAdmissionDocumentDate() != null) { - specification = specification - .and(buildRangeSpecification(criteria.getAdmissionDocumentDate(), Membership_.admissionDocumentDate)); - } - if (criteria.getCancellationDocumentDate() != null) { - specification = specification.and( - buildRangeSpecification(criteria.getCancellationDocumentDate(), Membership_.cancellationDocumentDate)); - } - if (criteria.getMemberFromDate() != null) { - specification = specification - .and(buildRangeSpecification(criteria.getMemberFromDate(), Membership_.memberFromDate)); - } - if (criteria.getMemberUntilDate() != null) { - specification = specification - .and(buildRangeSpecification(criteria.getMemberUntilDate(), Membership_.memberUntilDate)); - } - if (criteria.getRemark() != null) { - specification = specification.and(buildStringSpecification(criteria.getRemark(), Membership_.remark)); - } - if (criteria.getShareId() != null) { - specification = specification.and( - buildSpecification( - criteria.getShareId(), - root -> root.join(Membership_.shares, JoinType.LEFT).get(Share_.id))); - } - if (criteria.getAssetId() != null) { - specification = specification.and( - buildSpecification( - criteria.getAssetId(), - root -> root.join(Membership_.assets, JoinType.LEFT).get(Asset_.id))); - } - if (criteria.getCustomerId() != null) { - specification = specification.and( - buildSpecification( - criteria.getCustomerId(), - root -> root.join(Membership_.customer, JoinType.LEFT).get(Customer_.id))); - } - } - return specification; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/MembershipService.java b/src/main/java/org/hostsharing/hsadminng/service/MembershipService.java deleted file mode 100644 index 53f7a923..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/MembershipService.java +++ /dev/null @@ -1,103 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.service.dto.MembershipDTO; -import org.hostsharing.hsadminng.service.mapper.MembershipMapper; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; - -import javax.persistence.EntityManager; - -/** - * Service Implementation for managing Membership. - */ -@Service -@Transactional -public class MembershipService implements IdToDtoResolver { - - private final Logger log = LoggerFactory.getLogger(MembershipService.class); - - private final EntityManager em; - - private final MembershipValidator membershipValidator; - - private final MembershipRepository membershipRepository; - - private final MembershipMapper membershipMapper; - - public MembershipService( - final EntityManager em, - final MembershipValidator membershipValidator, - final MembershipRepository membershipRepository, - final MembershipMapper membershipMapper) { - this.em = em; - this.membershipValidator = membershipValidator; - this.membershipRepository = membershipRepository; - this.membershipMapper = membershipMapper; - } - - /** - * Save a membership. - * - * @param membershipDTO the entity to save - * @return the persisted entity - */ - public MembershipDTO save(MembershipDTO membershipDTO) { - log.debug("Request to save Membership : {}", membershipDTO); - - membershipValidator.validate(membershipDTO); - - Membership membership = membershipMapper.toEntity(membershipDTO); - membership = membershipRepository.save(membership); - em.flush(); - em.refresh(membership); - return membershipMapper.toDto(membership); - } - - /** - * Get all the memberships. - * - * @param pageable the pagination information - * @return the list of entities - */ - @Transactional(readOnly = true) - public Page findAll(Pageable pageable) { - log.debug("Request to get all Memberships"); - return membershipRepository.findAll(pageable) - .map(membershipMapper::toDto); - } - - /** - * Get one membership by id. - * - * @param id the id of the entity - * @return the entity - */ - @Override - @Transactional(readOnly = true) - public Optional findOne(Long id) { - log.debug("Request to get Membership : {}", id); - return membershipRepository.findById(id) - .map(membershipMapper::toDto); - } - - /** - * Prevent deleting a membership by id via service call. - * - * @param id the id of the entity - */ - public void delete(Long id) { - log.debug("Request to delete Membership : {}", id); - throw new BadRequestAlertException("Membership cannot be deleted", Membership.ENTITY_NAME, "membershipNotDeletable"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/MembershipValidator.java b/src/main/java/org/hostsharing/hsadminng/service/MembershipValidator.java deleted file mode 100644 index 9d8a33ad..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/MembershipValidator.java +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.service.dto.MembershipDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class MembershipValidator { - - @Autowired - private MembershipRepository membershipRepository; - - public void validate(final MembershipDTO membershipDTO) { - if (membershipDTO.getMemberUntilDate() != null - && !membershipDTO.getMemberUntilDate().isAfter(membershipDTO.getMemberFromDate())) { - throw new BadRequestAlertException("Invalid untilDate", Membership.ENTITY_NAME, "untilDateMustBeAfterSinceDate"); - } - - // It's known that this validation can cause a race condition if two memberships of the same customer are saved at - // same time (overlapping transactions). This is ignored in this case because it's too unlikely to be worth the effort. - if (membershipRepository.hasUncancelledMembershipForCustomer(membershipDTO.getCustomerId())) { - throw new BadRequestAlertException( - "Another uncancelled membership exists", - Membership.ENTITY_NAME, - "anotherUncancelledMembershipExists"); - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/SepaMandateQueryService.java b/src/main/java/org/hostsharing/hsadminng/service/SepaMandateQueryService.java deleted file mode 100644 index ba0e34b6..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/SepaMandateQueryService.java +++ /dev/null @@ -1,138 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.Customer_; -import org.hostsharing.hsadminng.domain.SepaMandate; -import org.hostsharing.hsadminng.domain.SepaMandate_; -import org.hostsharing.hsadminng.repository.SepaMandateRepository; -import org.hostsharing.hsadminng.service.dto.SepaMandateCriteria; -import org.hostsharing.hsadminng.service.dto.SepaMandateDTO; -import org.hostsharing.hsadminng.service.mapper.SepaMandateMapper; - -import io.github.jhipster.service.QueryService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -import javax.persistence.criteria.JoinType; - -/** - * Service for executing complex queries for SepaMandate entities in the database. - * The main input is a {@link SepaMandateCriteria} which gets converted to {@link Specification}, - * in a way that all the filters must apply. - * It returns a {@link List} of {@link SepaMandateDTO} or a {@link Page} of {@link SepaMandateDTO} which fulfills the criteria. - */ -@Service -@Transactional(readOnly = true) -public class SepaMandateQueryService extends QueryService { - - private final Logger log = LoggerFactory.getLogger(SepaMandateQueryService.class); - - private final SepaMandateRepository sepaMandateRepository; - - private final SepaMandateMapper sepaMandateMapper; - - public SepaMandateQueryService(SepaMandateRepository sepaMandateRepository, SepaMandateMapper sepaMandateMapper) { - this.sepaMandateRepository = sepaMandateRepository; - this.sepaMandateMapper = sepaMandateMapper; - } - - /** - * Return a {@link List} of {@link SepaMandateDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public List findByCriteria(SepaMandateCriteria criteria) { - log.debug("find by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return sepaMandateMapper.toDto(sepaMandateRepository.findAll(specification)); - } - - /** - * Return a {@link Page} of {@link SepaMandateDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @param page The page, which should be returned. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public Page findByCriteria(SepaMandateCriteria criteria, Pageable page) { - log.debug("find by criteria : {}, page: {}", criteria, page); - final Specification specification = createSpecification(criteria); - return sepaMandateRepository.findAll(specification, page) - .map(sepaMandateMapper::toDto); - } - - /** - * Return the number of matching entities in the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the number of matching entities. - */ - @Transactional(readOnly = true) - public long countByCriteria(SepaMandateCriteria criteria) { - log.debug("count by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return sepaMandateRepository.count(specification); - } - - /** - * Function to convert SepaMandateCriteria to a {@link Specification} - */ - private Specification createSpecification(SepaMandateCriteria criteria) { - Specification specification = Specification.where(null); - if (criteria != null) { - if (criteria.getId() != null) { - specification = specification.and(buildSpecification(criteria.getId(), SepaMandate_.id)); - } - if (criteria.getReference() != null) { - specification = specification.and(buildStringSpecification(criteria.getReference(), SepaMandate_.reference)); - } - if (criteria.getIban() != null) { - specification = specification.and(buildStringSpecification(criteria.getIban(), SepaMandate_.iban)); - } - if (criteria.getBic() != null) { - specification = specification.and(buildStringSpecification(criteria.getBic(), SepaMandate_.bic)); - } - if (criteria.getGrantingDocumentDate() != null) { - specification = specification - .and(buildRangeSpecification(criteria.getGrantingDocumentDate(), SepaMandate_.grantingDocumentDate)); - } - if (criteria.getRevokationDocumentDate() != null) { - specification = specification.and( - buildRangeSpecification(criteria.getRevokationDocumentDate(), SepaMandate_.revokationDocumentDate)); - } - if (criteria.getValidFromDate() != null) { - specification = specification - .and(buildRangeSpecification(criteria.getValidFromDate(), SepaMandate_.validFromDate)); - } - if (criteria.getValidUntilDate() != null) { - specification = specification - .and(buildRangeSpecification(criteria.getValidUntilDate(), SepaMandate_.validUntilDate)); - } - if (criteria.getLastUsedDate() != null) { - specification = specification - .and(buildRangeSpecification(criteria.getLastUsedDate(), SepaMandate_.lastUsedDate)); - } - if (criteria.getRemark() != null) { - specification = specification.and(buildStringSpecification(criteria.getRemark(), SepaMandate_.remark)); - } - if (criteria.getCustomerId() != null) { - specification = specification.and( - buildSpecification( - criteria.getCustomerId(), - root -> root.join(SepaMandate_.customer, JoinType.LEFT).get(Customer_.id))); - } - } - return specification; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/SepaMandateService.java b/src/main/java/org/hostsharing/hsadminng/service/SepaMandateService.java deleted file mode 100644 index 41900658..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/SepaMandateService.java +++ /dev/null @@ -1,94 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.SepaMandate; -import org.hostsharing.hsadminng.repository.SepaMandateRepository; -import org.hostsharing.hsadminng.service.dto.SepaMandateDTO; -import org.hostsharing.hsadminng.service.mapper.SepaMandateMapper; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; - -import javax.persistence.EntityManager; - -/** - * Service Implementation for managing SepaMandate. - */ -@Service -@Transactional -public class SepaMandateService implements IdToDtoResolver { - - private final Logger log = LoggerFactory.getLogger(SepaMandateService.class); - - private final EntityManager em; - - private final SepaMandateRepository sepaMandateRepository; - - private final SepaMandateMapper sepaMandateMapper; - - public SepaMandateService( - final EntityManager em, - final SepaMandateRepository sepaMandateRepository, - final SepaMandateMapper sepaMandateMapper) { - this.em = em; - this.sepaMandateRepository = sepaMandateRepository; - this.sepaMandateMapper = sepaMandateMapper; - } - - /** - * Save a sepaMandate. - * - * @param sepaMandateDTO the entity to save - * @return the persisted entity - */ - public SepaMandateDTO save(SepaMandateDTO sepaMandateDTO) { - log.debug("Request to save SepaMandate : {}", sepaMandateDTO); - SepaMandate sepaMandate = sepaMandateMapper.toEntity(sepaMandateDTO); - sepaMandate = sepaMandateRepository.save(sepaMandate); - em.flush(); - em.refresh(sepaMandate); - return sepaMandateMapper.toDto(sepaMandate); - } - - /** - * Get all the sepaMandates. - * - * @param pageable the pagination information - * @return the list of entities - */ - @Transactional(readOnly = true) - public Page findAll(Pageable pageable) { - log.debug("Request to get all SepaMandates"); - return sepaMandateRepository.findAll(pageable) - .map(sepaMandateMapper::toDto); - } - - /** - * Get one sepaMandate by id. - * - * @param id the id of the entity - * @return the entity - */ - @Transactional(readOnly = true) - public Optional findOne(Long id) { - log.debug("Request to get SepaMandate : {}", id); - return sepaMandateRepository.findById(id) - .map(sepaMandateMapper::toDto); - } - - /** - * Delete the sepaMandate by id. - * - * @param id the id of the entity - */ - public void delete(Long id) { - log.debug("Request to delete SepaMandate : {}", id); - sepaMandateRepository.deleteById(id); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/ShareQueryService.java b/src/main/java/org/hostsharing/hsadminng/service/ShareQueryService.java deleted file mode 100644 index 55f851e4..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/ShareQueryService.java +++ /dev/null @@ -1,120 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.*; -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.repository.ShareRepository; -import org.hostsharing.hsadminng.service.dto.ShareCriteria; -import org.hostsharing.hsadminng.service.dto.ShareDTO; -import org.hostsharing.hsadminng.service.mapper.ShareMapper; - -import io.github.jhipster.service.QueryService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -import javax.persistence.criteria.JoinType; - -/** - * Service for executing complex queries for Share entities in the database. - * The main input is a {@link ShareCriteria} which gets converted to {@link Specification}, - * in a way that all the filters must apply. - * It returns a {@link List} of {@link ShareDTO} or a {@link Page} of {@link ShareDTO} which fulfills the criteria. - */ -@Service -@Transactional(readOnly = true) -public class ShareQueryService extends QueryService { - - private final Logger log = LoggerFactory.getLogger(ShareQueryService.class); - - private final ShareRepository shareRepository; - - private final ShareMapper shareMapper; - - public ShareQueryService(ShareRepository shareRepository, ShareMapper shareMapper) { - this.shareRepository = shareRepository; - this.shareMapper = shareMapper; - } - - /** - * Return a {@link List} of {@link ShareDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public List findByCriteria(ShareCriteria criteria) { - log.debug("find by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return shareMapper.toDto(shareRepository.findAll(specification)); - } - - /** - * Return a {@link Page} of {@link ShareDTO} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @param page The page, which should be returned. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public Page findByCriteria(ShareCriteria criteria, Pageable page) { - log.debug("find by criteria : {}, page: {}", criteria, page); - final Specification specification = createSpecification(criteria); - return shareRepository.findAll(specification, page) - .map(shareMapper::toDto); - } - - /** - * Return the number of matching entities in the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the number of matching entities. - */ - @Transactional(readOnly = true) - public long countByCriteria(ShareCriteria criteria) { - log.debug("count by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return shareRepository.count(specification); - } - - /** - * Function to convert ShareCriteria to a {@link Specification} - */ - private Specification createSpecification(ShareCriteria criteria) { - Specification specification = Specification.where(null); - if (criteria != null) { - if (criteria.getId() != null) { - specification = specification.and(buildSpecification(criteria.getId(), Share_.id)); - } - if (criteria.getDocumentDate() != null) { - specification = specification.and(buildRangeSpecification(criteria.getDocumentDate(), Share_.documentDate)); - } - if (criteria.getValueDate() != null) { - specification = specification.and(buildRangeSpecification(criteria.getValueDate(), Share_.valueDate)); - } - if (criteria.getAction() != null) { - specification = specification.and(buildSpecification(criteria.getAction(), Share_.action)); - } - if (criteria.getQuantity() != null) { - specification = specification.and(buildRangeSpecification(criteria.getQuantity(), Share_.quantity)); - } - if (criteria.getRemark() != null) { - specification = specification.and(buildStringSpecification(criteria.getRemark(), Share_.remark)); - } - if (criteria.getMembershipId() != null) { - specification = specification.and( - buildSpecification( - criteria.getMembershipId(), - root -> root.join(Share_.membership, JoinType.LEFT).get(Membership_.id))); - } - } - return specification; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/ShareService.java b/src/main/java/org/hostsharing/hsadminng/service/ShareService.java deleted file mode 100644 index d7aa137d..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/ShareService.java +++ /dev/null @@ -1,103 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.repository.ShareRepository; -import org.hostsharing.hsadminng.service.dto.ShareDTO; -import org.hostsharing.hsadminng.service.mapper.ShareMapper; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; - -import javax.persistence.EntityManager; - -/** - * Service Implementation for managing Share. - */ -@Service -@Transactional -public class ShareService implements IdToDtoResolver { - - private final Logger log = LoggerFactory.getLogger(ShareService.class); - - private final EntityManager em; - - private final ShareRepository shareRepository; - - private final ShareMapper shareMapper; - - private final ShareValidator shareValidator; - - public ShareService( - final EntityManager em, - final ShareRepository shareRepository, - final ShareMapper shareMapper, - final ShareValidator shareValidator) { - this.em = em; - this.shareRepository = shareRepository; - this.shareMapper = shareMapper; - this.shareValidator = shareValidator; - } - - /** - * Save a share. - * - * @param shareDTO the entity to save - * @return the persisted entity - */ - public ShareDTO save(ShareDTO shareDTO) { - log.debug("Request to save Share : {}", shareDTO); - - shareValidator.validate(shareDTO); - - Share share = shareMapper.toEntity(shareDTO); - share = shareRepository.save(share); - em.flush(); - em.refresh(share); - return shareMapper.toDto(share); - } - - /** - * Get all the shares. - * - * @param pageable the pagination information - * @return the list of entities - */ - @Transactional(readOnly = true) - public Page findAll(Pageable pageable) { - log.debug("Request to get all Shares"); - return shareRepository.findAll(pageable) - .map(shareMapper::toDto); - } - - /** - * Get one share by id. - * - * @param id the id of the entity - * @return the entity - */ - @Transactional(readOnly = true) - public Optional findOne(Long id) { - log.debug("Request to get Share : {}", id); - return shareRepository.findById(id) - .map(shareMapper::toDto); - } - - /** - * Prevent deleting a share transaction by id via service call - * - * @param id the id of the entity - */ - public void delete(Long id) { - log.debug("Request to delete Share : {}", id); - - throw new BadRequestAlertException("Share transactions are immutable", Share.ENTITY_NAME, "shareTransactionImmutable"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/ShareValidator.java b/src/main/java/org/hostsharing/hsadminng/service/ShareValidator.java deleted file mode 100644 index 022ba196..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/ShareValidator.java +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; -import org.hostsharing.hsadminng.service.dto.ShareDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.springframework.stereotype.Service; - -@Service -public class ShareValidator { - - public void validate(final ShareDTO shareDTO) { - if (shareDTO.getId() != null) { - throw new BadRequestAlertException( - "Share transactions are immutable", - Share.ENTITY_NAME, - "shareTransactionImmutable"); - } - - if (shareDTO.getDocumentDate().isAfter(shareDTO.getValueDate())) { - throw new BadRequestAlertException( - "Document date may not be after value date", - Share.ENTITY_NAME, - "documentDateMayNotBeAfterValueDate"); - } - - if ((shareDTO.getAction() == ShareAction.SUBSCRIPTION) && (shareDTO.getQuantity() <= 0)) { - throw new BadRequestAlertException( - "Share subscriptions require a positive quantity", - Share.ENTITY_NAME, - "shareSubscriptionPositiveQuantity"); - } - - if ((shareDTO.getAction() == ShareAction.CANCELLATION) && (shareDTO.getQuantity() >= 0)) { - throw new BadRequestAlertException( - "Share cancellations require a negative quantity", - Share.ENTITY_NAME, - "shareCancellationNegativeQuantity"); - } - - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/UserRoleAssignmentQueryService.java b/src/main/java/org/hostsharing/hsadminng/service/UserRoleAssignmentQueryService.java deleted file mode 100644 index 19d7d780..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/UserRoleAssignmentQueryService.java +++ /dev/null @@ -1,112 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.domain.*; -import org.hostsharing.hsadminng.domain.UserRoleAssignment; -import org.hostsharing.hsadminng.repository.UserRoleAssignmentRepository; -import org.hostsharing.hsadminng.service.dto.UserRoleAssignmentCriteria; - -import io.github.jhipster.service.QueryService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -import javax.persistence.criteria.JoinType; - -/** - * Service for executing complex queries for UserRoleAssignment entities in the database. - * The main input is a {@link UserRoleAssignmentCriteria} which gets converted to {@link Specification}, - * in a way that all the filters must apply. - * It returns a {@link List} of {@link UserRoleAssignment} or a {@link Page} of {@link UserRoleAssignment} which fulfills the - * criteria. - */ -@Service -@Transactional(readOnly = true) -public class UserRoleAssignmentQueryService extends QueryService { - - private final Logger log = LoggerFactory.getLogger(UserRoleAssignmentQueryService.class); - - private final UserRoleAssignmentRepository userRoleAssignmentRepository; - - public UserRoleAssignmentQueryService(UserRoleAssignmentRepository userRoleAssignmentRepository) { - this.userRoleAssignmentRepository = userRoleAssignmentRepository; - } - - /** - * Return a {@link List} of {@link UserRoleAssignment} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public List findByCriteria(UserRoleAssignmentCriteria criteria) { - log.debug("find by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return userRoleAssignmentRepository.findAll(specification); - } - - /** - * Return a {@link Page} of {@link UserRoleAssignment} which matches the criteria from the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @param page The page, which should be returned. - * @return the matching entities. - */ - @Transactional(readOnly = true) - public Page findByCriteria(UserRoleAssignmentCriteria criteria, Pageable page) { - log.debug("find by criteria : {}, page: {}", criteria, page); - final Specification specification = createSpecification(criteria); - return userRoleAssignmentRepository.findAll(specification, page); - } - - /** - * Return the number of matching entities in the database - * - * @param criteria The object which holds all the filters, which the entities should match. - * @return the number of matching entities. - */ - @Transactional(readOnly = true) - public long countByCriteria(UserRoleAssignmentCriteria criteria) { - log.debug("count by criteria : {}", criteria); - final Specification specification = createSpecification(criteria); - return userRoleAssignmentRepository.count(specification); - } - - /** - * Function to convert UserRoleAssignmentCriteria to a {@link Specification} - */ - private Specification createSpecification(UserRoleAssignmentCriteria criteria) { - Specification specification = Specification.where(null); - if (criteria != null) { - if (criteria.getId() != null) { - specification = specification.and(buildSpecification(criteria.getId(), UserRoleAssignment_.id)); - } - if (criteria.getEntityTypeId() != null) { - specification = specification - .and(buildStringSpecification(criteria.getEntityTypeId(), UserRoleAssignment_.entityTypeId)); - } - if (criteria.getEntityObjectId() != null) { - specification = specification - .and(buildRangeSpecification(criteria.getEntityObjectId(), UserRoleAssignment_.entityObjectId)); - } - if (criteria.getAssignedRole() != null) { - specification = specification - .and(buildSpecification(criteria.getAssignedRole(), UserRoleAssignment_.assignedRole)); - } - if (criteria.getUserId() != null) { - specification = specification.and( - buildSpecification( - criteria.getUserId(), - root -> root.join(UserRoleAssignment_.user, JoinType.LEFT).get(User_.id))); - } - } - return specification; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/UserRoleAssignmentService.java b/src/main/java/org/hostsharing/hsadminng/service/UserRoleAssignmentService.java deleted file mode 100644 index 6f2d7664..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/UserRoleAssignmentService.java +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static com.google.common.base.Verify.verify; - -import org.hostsharing.hsadminng.domain.UserRoleAssignment; -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.repository.UserRoleAssignmentRepository; -import org.hostsharing.hsadminng.security.SecurityUtils; -import org.hostsharing.hsadminng.service.accessfilter.Role; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Collections; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Service Implementation for managing UserRoleAssignment. - */ -@Service -@Transactional -public class UserRoleAssignmentService implements IdToDtoResolver { - - private final Logger log = LoggerFactory.getLogger(UserRoleAssignmentService.class); - - private final UserRoleAssignmentRepository userRoleAssignmentRepository; - - public UserRoleAssignmentService( - final UserRepository userRepository, - final UserRoleAssignmentRepository userRoleAssignmentRepository) { - this.userRoleAssignmentRepository = userRoleAssignmentRepository; - } - - /** - * Save a userRoleAssignment. - * - * @param userRoleAssignment the entity to save - * @return the persisted entity - */ - public UserRoleAssignment save(UserRoleAssignment userRoleAssignment) { - log.debug("Request to save UserRoleAssignment : {}", userRoleAssignment); - return userRoleAssignmentRepository.save(userRoleAssignment); - } - - /** - * Get all the userRoleAssignments. - * - * @param pageable the pagination information - * @return the list of entities - */ - @Transactional(readOnly = true) - public Page findAll(Pageable pageable) { - log.debug("Request to get all UserRoleAssignments"); - return userRoleAssignmentRepository.findAll(pageable); - } - - /** - * Get one userRoleAssignment by id. - * - * @param id the id of the entity - * @return the entity - */ - @Transactional(readOnly = true) - public Optional findOne(Long id) { - log.debug("Request to get UserRoleAssignment : {}", id); - return userRoleAssignmentRepository.findById(id); - } - - /** - * Delete the userRoleAssignment by id. - * - * @param id the id of the entity - */ - public void delete(Long id) { - log.debug("Request to delete UserRoleAssignment : {}", id); - userRoleAssignmentRepository.deleteById(id); - } - - /** - * Collects all roles assigned to the current login user for the specified entity. - * - * @param entityTypeId the type id of the entity, e.g. "customer.Customer", not null - * @param entityObjectId id of entity instance in given entity type - * @return a set of all roles assigned to the current login user - */ - public Set getEffectiveRoleOfCurrentUser(final String entityTypeId, final long entityObjectId) { - verify(entityTypeId != null); - - // findByLogin is cached, thus I presume this is faster more specific query which would result in many DB roundtrips - final Set roles = SecurityUtils.getCurrentUserLogin() - .map( - login -> userRoleAssignmentRepository.findByLogin(login) - .stream() - .filter(ura -> matches(entityTypeId, entityObjectId, ura)) - .map(UserRoleAssignment::getAssignedRole) - .collect(Collectors.toSet())) - .orElse(Collections.emptySet()); - return roles; - } - - private static boolean matches(final String entityTypeId, final long entityObjectId, final UserRoleAssignment ura) { - return ura.getEntityTypeId().equals(entityTypeId) && ura.getEntityObjectId().equals(entityObjectId); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/UserService.java b/src/main/java/org/hostsharing/hsadminng/service/UserService.java deleted file mode 100644 index aab6278a..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/UserService.java +++ /dev/null @@ -1,302 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import org.hostsharing.hsadminng.config.Constants; -import org.hostsharing.hsadminng.domain.Authority; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.repository.AuthorityRepository; -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.security.SecurityUtils; -import org.hostsharing.hsadminng.service.dto.UserDTO; -import org.hostsharing.hsadminng.service.util.RandomUtil; -import org.hostsharing.hsadminng.web.rest.errors.*; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.cache.CacheManager; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.*; -import java.util.stream.Collectors; - -/** - * Service class for managing users. - */ -@Service -@Transactional -public class UserService { - - private final Logger log = LoggerFactory.getLogger(UserService.class); - - private final UserRepository userRepository; - - private final PasswordEncoder passwordEncoder; - - private final AuthorityRepository authorityRepository; - - private final CacheManager cacheManager; - - public UserService( - UserRepository userRepository, - PasswordEncoder passwordEncoder, - AuthorityRepository authorityRepository, - CacheManager cacheManager) { - this.userRepository = userRepository; - this.passwordEncoder = passwordEncoder; - this.authorityRepository = authorityRepository; - this.cacheManager = cacheManager; - } - - public Optional activateRegistration(String key) { - log.debug("Activating user for activation key {}", key); - return userRepository.findOneByActivationKey(key) - .map(user -> { - // activate given user for the registration key. - user.setActivated(true); - user.setActivationKey(null); - this.clearUserCaches(user); - log.debug("Activated user: {}", user); - return user; - }); - } - - public Optional completePasswordReset(String newPassword, String key) { - log.debug("Reset user password for reset key {}", key); - return userRepository.findOneByResetKey(key) - .filter(user -> user.getResetDate().isAfter(Instant.now().minusSeconds(86400))) - .map(user -> { - user.setPassword(passwordEncoder.encode(newPassword)); - user.setResetKey(null); - user.setResetDate(null); - this.clearUserCaches(user); - return user; - }); - } - - public Optional requestPasswordReset(String mail) { - return userRepository.findOneByEmailIgnoreCase(mail) - .filter(User::getActivated) - .map(user -> { - user.setResetKey(RandomUtil.generateResetKey()); - user.setResetDate(Instant.now()); - this.clearUserCaches(user); - return user; - }); - } - - public User registerUser(UserDTO userDTO, String password) { - userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).ifPresent(existingUser -> { - boolean removed = removeNonActivatedUser(existingUser); - if (!removed) { - throw new LoginAlreadyUsedException(); - } - }); - userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()).ifPresent(existingUser -> { - boolean removed = removeNonActivatedUser(existingUser); - if (!removed) { - throw new EmailAlreadyUsedException(); - } - }); - User newUser = new User(); - String encryptedPassword = passwordEncoder.encode(password); - newUser.setLogin(userDTO.getLogin().toLowerCase()); - // new user gets initially a generated password - newUser.setPassword(encryptedPassword); - newUser.setFirstName(userDTO.getFirstName()); - newUser.setLastName(userDTO.getLastName()); - newUser.setEmail(userDTO.getEmail().toLowerCase()); - newUser.setImageUrl(userDTO.getImageUrl()); - newUser.setLangKey(userDTO.getLangKey()); - // new user is not active - newUser.setActivated(false); - // new user gets registration key - newUser.setActivationKey(RandomUtil.generateActivationKey()); - Set authorities = new HashSet<>(); - authorityRepository.findById(AuthoritiesConstants.USER).ifPresent(authorities::add); - newUser.setAuthorities(authorities); - userRepository.save(newUser); - this.clearUserCaches(newUser); - log.debug("Created Information for User: {}", newUser); - return newUser; - } - - private boolean removeNonActivatedUser(User existingUser) { - if (existingUser.getActivated()) { - return false; - } - userRepository.delete(existingUser); - userRepository.flush(); - this.clearUserCaches(existingUser); - return true; - } - - public User createUser(UserDTO userDTO) { - User user = new User(); - user.setLogin(userDTO.getLogin().toLowerCase()); - user.setFirstName(userDTO.getFirstName()); - user.setLastName(userDTO.getLastName()); - user.setEmail(userDTO.getEmail().toLowerCase()); - user.setImageUrl(userDTO.getImageUrl()); - if (userDTO.getLangKey() == null) { - user.setLangKey(Constants.DEFAULT_LANGUAGE); // default language - } else { - user.setLangKey(userDTO.getLangKey()); - } - String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword()); - user.setPassword(encryptedPassword); - user.setResetKey(RandomUtil.generateResetKey()); - user.setResetDate(Instant.now()); - user.setActivated(true); - if (userDTO.getAuthorities() != null) { - Set authorities = userDTO.getAuthorities() - .stream() - .map(authorityRepository::findById) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toSet()); - user.setAuthorities(authorities); - } - userRepository.save(user); - this.clearUserCaches(user); - log.debug("Created Information for User: {}", user); - return user; - } - - /** - * Update basic information (first name, last name, email, language) for the current user. - * - * @param firstName first name of user - * @param lastName last name of user - * @param email email id of user - * @param langKey language key - * @param imageUrl image URL of user - */ - public void updateUser(String firstName, String lastName, String email, String langKey, String imageUrl) { - SecurityUtils.getCurrentUserLogin() - .flatMap(userRepository::findOneByLogin) - .ifPresent(user -> { - user.setFirstName(firstName); - user.setLastName(lastName); - user.setEmail(email.toLowerCase()); - user.setLangKey(langKey); - user.setImageUrl(imageUrl); - this.clearUserCaches(user); - log.debug("Changed Information for User: {}", user); - }); - } - - /** - * Update all information for a specific user, and return the modified user. - * - * @param userDTO user to update - * @return updated user - */ - public Optional updateUser(UserDTO userDTO) { - return Optional.of( - userRepository - .findById(userDTO.getId())) - .filter(Optional::isPresent) - .map(Optional::get) - .map(user -> { - this.clearUserCaches(user); - user.setLogin(userDTO.getLogin().toLowerCase()); - user.setFirstName(userDTO.getFirstName()); - user.setLastName(userDTO.getLastName()); - user.setEmail(userDTO.getEmail().toLowerCase()); - user.setImageUrl(userDTO.getImageUrl()); - user.setActivated(userDTO.isActivated()); - user.setLangKey(userDTO.getLangKey()); - Set managedAuthorities = user.getAuthorities(); - managedAuthorities.clear(); - userDTO.getAuthorities() - .stream() - .map(authorityRepository::findById) - .filter(Optional::isPresent) - .map(Optional::get) - .forEach(managedAuthorities::add); - this.clearUserCaches(user); - log.debug("Changed Information for User: {}", user); - return user; - }) - .map(UserDTO::new); - } - - public void deleteUser(String login) { - userRepository.findOneByLogin(login).ifPresent(user -> { - userRepository.delete(user); - this.clearUserCaches(user); - log.debug("Deleted User: {}", user); - }); - } - - public void changePassword(String currentClearTextPassword, String newPassword) { - SecurityUtils.getCurrentUserLogin() - .flatMap(userRepository::findOneByLogin) - .ifPresent(user -> { - String currentEncryptedPassword = user.getPassword(); - if (!passwordEncoder.matches(currentClearTextPassword, currentEncryptedPassword)) { - throw new InvalidPasswordException(); - } - String encryptedPassword = passwordEncoder.encode(newPassword); - user.setPassword(encryptedPassword); - this.clearUserCaches(user); - log.debug("Changed password for User: {}", user); - }); - } - - @Transactional(readOnly = true) - public Page getAllManagedUsers(Pageable pageable) { - return userRepository.findAllByLoginNot(pageable, Constants.ANONYMOUS_USER).map(UserDTO::new); - } - - @Transactional(readOnly = true) - public Optional getUserWithAuthoritiesByLogin(String login) { - return userRepository.findOneWithAuthoritiesByLogin(login); - } - - @Transactional(readOnly = true) - public Optional getUserWithAuthorities(Long id) { - return userRepository.findOneWithAuthoritiesById(id); - } - - @Transactional(readOnly = true) - public Optional getUserWithAuthorities() { - return SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneWithAuthoritiesByLogin); - } - - /** - * Not activated users should be automatically deleted after 3 days. - *

- * This is scheduled to get fired everyday, at 01:00 (am). - */ - @Scheduled(cron = "0 0 1 * * ?") - public void removeNotActivatedUsers() { - userRepository - .findAllByActivatedIsFalseAndCreatedDateBefore(Instant.now().minus(3, ChronoUnit.DAYS)) - .forEach(user -> { - log.debug("Deleting not activated user {}", user.getLogin()); - userRepository.delete(user); - this.clearUserCaches(user); - }); - } - - /** - * @return a list of all the authorities - */ - public List getAuthorities() { - return authorityRepository.findAll().stream().map(Authority::getName).collect(Collectors.toList()); - } - - private void clearUserCaches(User user) { - Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE)).evict(user.getLogin()); - Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE)).evict(user.getEmail()); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessFor.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessFor.java deleted file mode 100644 index 0d0a1a52..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessFor.java +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import org.hostsharing.hsadminng.service.accessfilter.Role.Nobody; - -import java.lang.annotation.*; - -@Documented -@Target({ ElementType.FIELD, ElementType.TYPE_USE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface AccessFor { - - Class[] init() default Nobody.class; - - Class[] update() default Nobody.class; - - Class[] read() default Nobody.class; -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessMappings.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessMappings.java deleted file mode 100644 index af8f2c74..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessMappings.java +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import java.io.Serializable; - -/** - * A marker interface for DTO classes which can be used by {@link JsonSerializerWithAccessFilter} and - * {@link JsonDeserializerWithAccessFilter}. - */ -public interface AccessMappings extends Serializable { - - Long getId(); -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/EntityTypeId.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/EntityTypeId.java deleted file mode 100644 index 3ba2792d..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/EntityTypeId.java +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import java.lang.annotation.*; - -/** - * Specifies the entityTypeId to be used in UserRoleAssignment. - */ -@Documented -@Target({ ElementType.FIELD, ElementType.TYPE_USE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface EntityTypeId { - - /** - * The ID of the entity type, max length: 32. - * - * Pattern: "module.Entity", e.g. "customer.Membership" - */ - String value(); -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilter.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilter.java deleted file mode 100644 index a765138d..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilter.java +++ /dev/null @@ -1,149 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static com.google.common.base.Verify.verify; -import static com.google.common.collect.Sets.union; -import static java.util.Collections.EMPTY_SET; -import static java.util.Collections.emptySet; - -import org.hostsharing.hsadminng.security.SecurityUtils; -import org.hostsharing.hsadminng.service.IdToDtoResolver; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.dto.MembershipDTO; -import org.hostsharing.hsadminng.service.util.ReflectionUtil; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.util.Set; -import java.util.stream.Collectors; - -abstract class JSonAccessFilter { - - private final ApplicationContext ctx; - private final UserRoleAssignmentService userRoleAssignmentService; - - final T dto; - final Field selfIdField; - final Field parentIdField; - - JSonAccessFilter(final ApplicationContext ctx, final UserRoleAssignmentService userRoleAssignmentService, final T dto) { - this.ctx = ctx; - this.userRoleAssignmentService = userRoleAssignmentService; - this.dto = dto; - this.selfIdField = determineFieldWithAnnotation(dto.getClass(), SelfId.class); - this.parentIdField = determineFieldWithAnnotation(dto.getClass(), ParentId.class); - } - - Long getId() { - if (selfIdField == null) { - return null; - } - return (Long) ReflectionUtil.getValue(dto, selfIdField); - } - - /** - * @param field to get a display representation for - * @return a simplified, decently user readable, display representation of the given field - */ - String toDisplay(final Field field) { - return field.getDeclaringClass().getSimpleName() + "." + field.getName(); - } - - /** - * @return all roles of the login user in relation to the dto, for which this filter is created. - */ - Set getLoginUserRoles() { - final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null) { - return emptySet(); - } - final Set independentRoles = authentication - .getAuthorities() - .stream() - .map(GrantedAuthority::getAuthority) - .map(Role::of) - .collect(Collectors.toSet()); - - final Set rolesOnThis = getId() != null ? getLoginUserDirectRolesFor(dto.getClass(), getId()) : EMPTY_SET; - return union(independentRoles, union(rolesOnThis, getLoginUserRoleOnAncestorIfHigher(dto))); - } - - private Set getLoginUserRoleOnAncestorIfHigher(final Object dto) { - final Field parentIdField = determineFieldWithAnnotation(dto.getClass(), ParentId.class); - - if (parentIdField == null) { - return emptySet(); - } - - final ParentId parentIdAnnot = parentIdField.getAnnotation(ParentId.class); - final Class parentDtoLoader = parentIdAnnot.resolver(); - final Class rawType = IdToDtoResolver.class; - - final Class parentDtoClass = ReflectionUtil. determineGenericInterfaceParameter(parentDtoLoader, rawType, 0); - final Object parent = ReflectionUtil.getValue(dto, parentIdField); - if (parent == null) { - return emptySet(); - } - final Long parentId = parent instanceof AccessMappings ? (((AccessMappings) parent).getId()) : (Long) parent; - final Set rolesOnParent = getLoginUserDirectRolesFor(parentDtoClass, parentId); - - final Object parentEntity = loadDto(parentDtoLoader, parentId); - return union(rolesOnParent, getLoginUserRoleOnAncestorIfHigher(parentEntity)); - } - - private Set getLoginUserDirectRolesFor(final Class dtoClass, final long id) { - verify(SecurityUtils.isAuthenticated()); - - final EntityTypeId entityTypeId = dtoClass.getAnnotation(EntityTypeId.class); - verify(entityTypeId != null, "@" + EntityTypeId.class.getSimpleName() + " missing on " + dtoClass.getName()); - - return userRoleAssignmentService.getEffectiveRoleOfCurrentUser(entityTypeId.value(), id); - } - - @SuppressWarnings("unchecked") - protected Object loadDto(final Class resolverClass, final Long id) { - verify(id != null, "id must not be null for " + resolverClass.getSimpleName()); - - final AutowireCapableBeanFactory beanFactory = ctx.getAutowireCapableBeanFactory(); - verify( - beanFactory != null, - "no bean factory found, probably missing mock configuration for ApplicationContext, e.g. given(...)"); - - final IdToDtoResolver resolverBean = beanFactory.createBean(resolverClass); - verify( - resolverBean != null, - "no " + resolverClass.getSimpleName() - + " bean created, probably missing mock configuration for AutowireCapableBeanFactory, e.g. given(...)"); - - return resolverBean.findOne(id) - .orElseThrow( - () -> new BadRequestAlertException( - "Can't resolve entity ID " + id + " via " + resolverClass, - resolverClass.getSimpleName(), - "isNotFound")); - } - - private static Field determineFieldWithAnnotation( - final Class dtoClass, - final Class idAnnotationClass) { - Field parentIdField = null; - for (Field field : dtoClass.getDeclaredFields()) { - if (field.isAnnotationPresent(idAnnotationClass)) { - if (parentIdField != null) { - throw new AssertionError( - "multiple @" + idAnnotationClass.getSimpleName() + " detected in " - + field.getDeclaringClass().getSimpleName()); - } - parentIdField = field; - } - } - return parentIdField; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonFieldReader.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonFieldReader.java deleted file mode 100644 index 51d1d063..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonFieldReader.java +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -/** - * Reads a JSON node value. - * - * @param - */ -@FunctionalInterface -public interface JSonFieldReader { - - /** - * Reads a JSON node value. - * - * @param target your target entity or DTO type - * - */ - void readInto(T target); -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonFieldWriter.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonFieldWriter.java deleted file mode 100644 index 6f93a04b..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonFieldWriter.java +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import com.fasterxml.jackson.core.JsonGenerator; - -import java.io.IOException; - -/** - * Similar to a BiConsumer, but declaring IOException as needed by JsonGenerator. - * - * @param - */ -@FunctionalInterface -public interface JSonFieldWriter { - - /** - * Writes a JSON field and value. - * - * @param object your entity or DTO type - * @param jsonGenerator provides low level methods for writing JSON fields - */ - void write(T object, JsonGenerator jsonGenerator) throws IOException; -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonDeserializerWithAccessFilter.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonDeserializerWithAccessFilter.java deleted file mode 100644 index afe7dc32..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonDeserializerWithAccessFilter.java +++ /dev/null @@ -1,288 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static com.google.common.base.Verify.verify; -import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked; - -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.util.ReflectionUtil; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.TreeNode; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.*; -import com.google.common.base.Joiner; - -import org.apache.commons.lang3.NotImplementedException; -import org.apache.commons.lang3.ObjectUtils; -import org.springframework.context.ApplicationContext; - -import java.lang.reflect.Field; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.HashSet; -import java.util.Set; - -public abstract class JsonDeserializerWithAccessFilter extends JsonDeserializer { - - private final ApplicationContext ctx; - private final UserRoleAssignmentService userRoleAssignmentService; - - public JsonDeserializerWithAccessFilter( - final ApplicationContext ctx, - final UserRoleAssignmentService userRoleAssignmentService) { - this.ctx = ctx; - this.userRoleAssignmentService = userRoleAssignmentService; - } - - @Override - public T deserialize( - final JsonParser jsonParser, - final DeserializationContext deserializationContext) { - - final Class dtoClass = ReflectionUtil - .determineGenericClassParameter(this.getClass(), JsonDeserializerWithAccessFilter.class, 0); - // @formatter:off - return new JSonDeserializationWithAccessFilter( - this, ctx, userRoleAssignmentService, jsonParser, deserializationContext, dtoClass) - .deserialize(); - // @formatter:on - } - - protected JSonFieldReader jsonFieldReader(final TreeNode treeNode, final Field field) { - return (final T object) -> { - final Object newValue = readValueFromJSon(treeNode, field); - writeValueToDto(object, field, newValue); - }; - } - - /** - * Returns the named subnode of the given node. - *

- * If entities are used instead of DTOs, JHipster will generate code which sends - * complete entity trees to the REST endpoint. In most cases, we only need the "id", - * though. - *

- * - * @param node the JSON node of which a subnode is to be returned - * @param name the name of the subnode within 'node' - * @return the subnode of 'node' with the given 'name' - */ - protected final JsonNode getSubNode(final TreeNode node, final String name) { - verify(node.isObject(), node + " is not a JSON object"); - final ObjectNode objectNode = (ObjectNode) node; - final JsonNode subNode = objectNode.get(name); - verify(subNode.isNumber(), node + "." + name + " is not a number"); - return subNode; - } - - private Object readValueFromJSon(final TreeNode treeNode, final Field field) { - return readValueFromJSon(treeNode, field.getName(), field.getType()); - } - - private Object readValueFromJSon(final TreeNode treeNode, final String fieldName, final Class fieldClass) { - // FIXME can be removed? final TreeNode fieldNode = treeNode.get(fieldName); - if (treeNode instanceof NullNode) { - return null; - } - if (treeNode instanceof TextNode) { - return ((TextNode) treeNode).asText(); - } - if (treeNode instanceof IntNode) { - return ((IntNode) treeNode).asInt(); - } - if (treeNode instanceof LongNode) { - return ((LongNode) treeNode).asLong(); - } - if (treeNode instanceof DoubleNode) { - // TODO: we need to figure out, why DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS does not work - return ((DoubleNode) treeNode).asDouble(); - } - if (treeNode instanceof ArrayNode && LocalDate.class.isAssignableFrom(fieldClass)) { - return LocalDate.of( - ((ArrayNode) treeNode).get(0).asInt(), - ((ArrayNode) treeNode).get(1).asInt(), - ((ArrayNode) treeNode).get(2).asInt()); - } - throw new NotImplementedException( - "JSon node type not implemented: " + treeNode.getClass() + " -> " + fieldName + ": " + fieldClass); - } - - private void writeValueToDto(final T dto, final Field field, final Object value) { - if (value == null) { - ReflectionUtil.setValue(dto, field, null); - } else if (field.getType().isAssignableFrom(value.getClass())) { - ReflectionUtil.setValue(dto, field, value); - } else if (int.class.isAssignableFrom(field.getType())) { - ReflectionUtil.setValue(dto, field, ((Number) value).intValue()); - } else if (Long.class.isAssignableFrom(field.getType()) || long.class.isAssignableFrom(field.getType())) { - ReflectionUtil.setValue(dto, field, ((Number) value).longValue()); - } else if (BigDecimal.class.isAssignableFrom(field.getType())) { - ReflectionUtil.setValue(dto, field, new BigDecimal(value.toString())); - } else if (Boolean.class.isAssignableFrom(field.getType()) || boolean.class.isAssignableFrom(field.getType())) { - ReflectionUtil.setValue(dto, field, Boolean.valueOf(value.toString())); - } else if (field.getType().isEnum()) { - ReflectionUtil.setValue(dto, field, ReflectionUtil.asEnumValue(field.getType(), value)); - } else if (LocalDate.class.isAssignableFrom(field.getType())) { - ReflectionUtil.setValue(dto, field, LocalDate.parse(value.toString())); - } else { - throw new NotImplementedException("property type not yet implemented: " + field); - } - } - - /** - * Internal implementation of JSON deserialization, where {@link JsonDeserializerWithAccessFilter} - * is a stateless bean, this inner class exists only during the actual deserialization and contains - * the deserialization state. - */ - private class JSonDeserializationWithAccessFilter extends JSonAccessFilter { - - private final TreeNode treeNode; - private final Set updatingFields = new HashSet<>(); - - public JSonDeserializationWithAccessFilter( - final JsonDeserializerWithAccessFilter deserializer, - final ApplicationContext ctx, - final UserRoleAssignmentService userRoleAssignmentService, - final JsonParser jsonParser, - final DeserializationContext deserializationContext, - Class dtoClass) { - super(ctx, userRoleAssignmentService, unchecked(dtoClass::newInstance)); - this.treeNode = unchecked(() -> jsonParser.getCodec().readTree(jsonParser)); - } - - // Jackson deserializes from the JsonParser, thus no input parameter needed. - public T deserialize() { - deserializeValues(); - final T currentDto = loadCurrentDto(getId()); - overwriteUnmodifiedFieldsWithCurrentValues(currentDto); - checkAccessToWrittenFields(currentDto); - return dto; - } - - private void deserializeValues() { - treeNode.fieldNames().forEachRemaining(fieldName -> { - try { - final Field field = dto.getClass().getDeclaredField(fieldName); - final TreeNode node = treeNode.get(fieldName); - jsonFieldReader(node, field).readInto(dto); - updatingFields.add(field); - } catch (NoSuchFieldException e) { - throw new BadRequestAlertException("Unknown property", fieldName, "unknownProperty"); - } - }); - } - - @SuppressWarnings("unchecked") - private T loadCurrentDto(final Long id) { - if (id != null) { - return (T) loadDto(selfIdField.getAnnotation(SelfId.class).resolver(), id); - } - return null; - } - - private void overwriteUnmodifiedFieldsWithCurrentValues(final T currentDto) { - if (currentDto == null) { - return; - } - for (Field field : currentDto.getClass().getDeclaredFields()) { - if (field.isAnnotationPresent(AccessFor.class)) { - boolean updatingField = updatingFields.contains(field); - if (updatingField && !isActuallyUpdated(field, dto, currentDto)) { - updatingFields.remove(field); - updatingField = false; - } - if (!updatingField) { - final Object value = ReflectionUtil.getValue(currentDto, field); - ReflectionUtil.setValue(dto, field, value); - } - } - - } - } - - private void checkAccessToWrittenFields(final T currentDto) { - updatingFields.forEach( - field -> { - final Set roles = getLoginUserRoles(); - if (isInitAccess()) { - validateInitAccess(field, roles); - } else { - validateUpdateAccess(field, roles); - } - }); - } - - private void validateInitAccess(Field field, Set roles) { - if (!Role.toBeIgnoredForUpdates(field) && !isAllowedToInit(roles, field)) { - if (!field.equals(parentIdField)) { - throw new BadRequestAlertException( - "Initialization of field " + toDisplay(field) - + " prohibited for current user role(s): " - + asString(roles), - toDisplay(field), - "initializationProhibited"); - } else { - throw new BadRequestAlertException( - "Referencing field " + toDisplay(field) - + " prohibited for current user role(s): " - + asString(roles), - toDisplay(field), - "referencingProhibited"); - } - } - } - - private String asString(Set roles) { - return Joiner.on("+").join(roles.stream().map(Role::name).toArray()); - } - - private void validateUpdateAccess(Field field, Set roles) { - if (!Role.toBeIgnoredForUpdates(field) && !isAllowedToUpdate(getLoginUserRoles(), field)) { - throw new BadRequestAlertException( - "Update of field " + toDisplay(field) + " prohibited for current user role(s): " - + asString(roles), - toDisplay(field), - "updateProhibited"); - } - } - - private boolean isAllowedToInit(final Set roles, final Field field) { - for (Role role : roles) { - if (role.isAllowedToInit(field)) { - return true; - } - } - return false; - } - - private boolean isAllowedToUpdate(final Set roles, final Field field) { - for (Role role : roles) { - if (role.isAllowedToUpdate(field)) { - return true; - } - } - return false; - } - - private boolean isInitAccess() { - return getId() == null; - } - - private boolean isActuallyUpdated(final Field field, final T dto, T currentDto) { - final Object o1 = ReflectionUtil.getValue(dto, field); - final Object o2 = ReflectionUtil.getValue(currentDto, field); - - if (o1 instanceof Comparable && o2 instanceof Comparable) { - verify( - o2 instanceof Comparable, - "either neither or both objects must implement Comparable"); // $COVERAGE-IGNORE$ - return 0 != ((Comparable) o1).compareTo(o2); - } - return ObjectUtils.notEqual(o1, o2); - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonSerializerWithAccessFilter.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonSerializerWithAccessFilter.java deleted file mode 100644 index b8849182..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonSerializerWithAccessFilter.java +++ /dev/null @@ -1,138 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.util.ReflectionUtil; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; - -import org.apache.commons.lang3.NotImplementedException; -import org.springframework.context.ApplicationContext; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.Set; - -/** - * A base class for a Spring bean for JSON serialization with field-based access filters. - * Where {@link JSonSerializationWithAccessFilter} is the actual stateful implementation and - * it's instances only exist during the process of serialization, this class is a stateless just - * used for service and context injection. - * - * @param DTO class to serialize - */ -public abstract class JsonSerializerWithAccessFilter extends JsonSerializer { - - protected final ApplicationContext ctx; - protected final UserRoleAssignmentService userRoleAssignmentService; - - public JsonSerializerWithAccessFilter( - final ApplicationContext ctx, - final UserRoleAssignmentService userRoleAssignmentService) { - this.ctx = ctx; - this.userRoleAssignmentService = userRoleAssignmentService; - } - - @Override - public void serialize( - final T dto, - final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider) throws IOException { - - new JSonSerializationWithAccessFilter(this, ctx, userRoleAssignmentService, jsonGenerator, serializerProvider, dto) - .serialize(); - } - - protected JSonFieldWriter jsonFieldWriter(final Field field) { - - return (final T dto, final JsonGenerator jsonGenerator) -> { - final String fieldName = field.getName(); - final Object fieldValue = ReflectionUtil.getValue(dto, field); - // TODO mhoennig turn this into a dispatch table? - // TODO mhoennig: or maybe replace by serializerProvider.defaultSerialize...()? - // But the latter makes it difficult for parallel structure with the deserializer (clumsy API). - // Alternatively extract the supported types to subclasses of some abstract class and - // here as well as in the deserializer just access the matching implementation through a map. - // Or even completely switch from Jackson to GSON? - - if (fieldValue == null) { - jsonGenerator.writeNullField(fieldName); - } else if (String.class.isAssignableFrom(field.getType())) { - jsonGenerator.writeStringField(fieldName, (String) fieldValue); - } else if (Integer.class.isAssignableFrom(field.getType()) || int.class.isAssignableFrom(field.getType())) { - jsonGenerator.writeNumberField(fieldName, (int) fieldValue); - } else if (Long.class.isAssignableFrom(field.getType()) || long.class.isAssignableFrom(field.getType())) { - jsonGenerator.writeNumberField(fieldName, (long) fieldValue); - } else if (LocalDate.class.isAssignableFrom(field.getType())) { - jsonGenerator.writeStringField(fieldName, fieldValue.toString()); - } else if (Enum.class.isAssignableFrom(field.getType())) { - jsonGenerator.writeStringField(fieldName, ((Enum) fieldValue).name()); - } else if (Boolean.class.isAssignableFrom(field.getType()) || boolean.class.isAssignableFrom(field.getType())) { - jsonGenerator.writeBooleanField(fieldName, (Boolean) fieldValue); - } else if (BigDecimal.class.isAssignableFrom(field.getType())) { - jsonGenerator.writeNumberField(fieldName, (BigDecimal) fieldValue); - } else { - throw new NotImplementedException("property type not yet implemented: " + field); - } - }; - } - - /** - * INTERNAL implementation of JSON serialization, where {@link JsonSerializerWithAccessFilter} - * is a stateless bean, this inner class exists only during the actual serialization and - * contains a serialization state. - */ - private class JSonSerializationWithAccessFilter extends JSonAccessFilter { - - private final JsonSerializerWithAccessFilter serializer; - private final JsonGenerator jsonGenerator; - private final SerializerProvider serializerProvider; - - public JSonSerializationWithAccessFilter( - final JsonSerializerWithAccessFilter serializer, - final ApplicationContext ctx, - final UserRoleAssignmentService userRoleAssignmentService, - final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider, - final T dto) { - super(ctx, userRoleAssignmentService, dto); - this.serializer = serializer; - this.jsonGenerator = jsonGenerator; - this.serializerProvider = serializerProvider; - } - - // Jackson serializes into the JsonGenerator, thus no return value needed. - public void serialize() throws IOException { - - jsonGenerator.writeStartObject(); - for (Field field : dto.getClass().getDeclaredFields()) { - toJSon(dto, jsonGenerator, field); - } - jsonGenerator.writeEndObject(); - } - - protected void writeJSonField(final T dto, final Field field, final JsonGenerator jsonGenerator) throws IOException { - serializer.jsonFieldWriter(field).write(dto, jsonGenerator); - } - - private void toJSon(final T dto, final JsonGenerator jsonGenerator, final Field field) throws IOException { - if (isAllowedToRead(getLoginUserRoles(), field)) { - writeJSonField(dto, field, jsonGenerator); - } - } - - private boolean isAllowedToRead(final Set roles, final Field field) { - for (Role role : roles) { - if (role.isAllowedToRead(field)) { - return true; - } - } - return ReflectionUtil.newInstance(Role.Anybody.class).isAllowedToRead(field); // TODO - } - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/ParentId.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/ParentId.java deleted file mode 100644 index 9dd46a0a..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/ParentId.java +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import org.hostsharing.hsadminng.service.IdToDtoResolver; - -import java.lang.annotation.*; - -/** - * Used to mark a field within a DTO as containing the id of a referenced entity, - * it's needed to determine access rights for entity creation. - * - * @see AccessFor - */ -@Documented -@Target({ ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface ParentId { - - /// The service which can load the referenced DTO. - Class> resolver(); -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/Role.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/Role.java deleted file mode 100644 index c14cb531..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/Role.java +++ /dev/null @@ -1,437 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static com.google.common.base.Verify.verify; -import static org.hostsharing.hsadminng.service.util.ReflectionUtil.initialize; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.domain.UserRoleAssignment; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.util.ReflectionUtil; - -import org.apache.commons.lang3.ArrayUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.Map; - -/** - * These enum values are used to specify the minimum role required to grant access to resources, - * see usages of {@link AccessFor}. - * Also they can be assigned to users via {@link UserRoleAssignment}. - * Some of the concrete values make only sense in one of these contexts. - *

- * There are two kinds of roles: independent and dependent. - * Independent roles like {@link Hostmaster} are absolute roles which means unrelated to any concrete entity. - * Dependent roles like {@link CustomerContractualContact} are relative to a specific entity, - * in this case to a specific {@link Customer}. - *

- *

- * Separate classes are used to make it possible to use roles in Java annotations - * and also make it possible to have roles spread over multiple modules. - *

- */ -public abstract class Role { - - private static final Logger log = LoggerFactory.getLogger(Role.class); - - // TODO mhoennig: We need to make sure that the classes are loaded - // and thus the static initializers were called - // before these maps are used in production code. - private static Map, Role> rolesByClass = new HashMap<>(); - private static Map rolesByName = new HashMap<>(); - - private final String authority; - private final LazyRoles comprises; - - Role() { - this.authority = AuthoritiesConstants.USER; - // noinspection unchecked - this.comprises = new LazyRoles(); - } - - @SafeVarargs - Role(final Class... comprisedRoleClasses) { - this.authority = AuthoritiesConstants.USER; - // noinspection unchecked - this.comprises = new LazyRoles(comprisedRoleClasses); - } - - @SafeVarargs - Role(final String authority, final Class... comprisedRoleClasses) { - this.authority = authority; - // noinspection unchecked - this.comprises = new LazyRoles(comprisedRoleClasses); - } - - public static Role of(final String authority) { - final Role role = rolesByName.get(authority); - verify( - role != null, - "unknown authority: %s, available authorities: ", - authority, - ArrayUtils.toString(rolesByName.keySet())); - return role; - } - - public static T of(final Class roleClass) { - // prevent initialization and thus recursive call to `Role.of(...)` within `newInstance(...)` - final Class initializedRoleClass = initialize(roleClass); - { - final T role = (T) rolesByClass.get(initializedRoleClass); - if (role != null) { - return role; - } - } - { - T newRole = (T) ReflectionUtil.newInstance(initializedRoleClass); - rolesByClass.put(initializedRoleClass, newRole); - rolesByName.put(newRole.name(), newRole); - log.info("Role registered: {} as {}", initializedRoleClass, newRole.name()); - return newRole; - } - } - - public static void init() { - Role.of(Anybody.class); - Role.of(Hostmaster.class); - Role.of(Admin.class); - Role.of(Supporter.class); - Role.of(AnyCustomerContact.class); - Role.of(CustomerContractualContact.class); - Role.of(CustomerTechnicalContact.class); - Role.of(CustomerFinancialContact.class); - Role.of(AnyCustomerUser.class); - Role.of(ActualCustomerUser.class); - Role.of(Ignored.class); - Role.of(Nobody.class); - } - - @Override - public String toString() { - return getClass().getName() + "(" + name() + ")"; - } - - public abstract String name(); - - public static class IndependentRole extends Role { - - @SafeVarargs - IndependentRole(final String authority, final Class... comprisedRoleClasses) { - super(authority, comprisedRoleClasses); - } - - public String name() { - return authority(); - } - } - - public static class DependentRole extends Role { - - DependentRole() { - } - - @SafeVarargs - DependentRole(final Class... comprisedRoleClasses) { - super(AuthoritiesConstants.USER, comprisedRoleClasses); - } - - public String name() { - return getClass().getSimpleName(); // TODO: decide if it's ok for use in the DB table - } - } - - /** - * Default for access rights requirement. You can read it as: 'Nobody is allowed to ...'. - * This is usually used for fields which are managed by hsadminNg itself. - *

- * This role cannot be assigned to a user. - *

- */ - public static class Nobody extends DependentRole { - - public static final Nobody ROLE = Role.of(Nobody.class); - } - - /** - * Hostmasters are initialize/update/read and field which, except where NOBODY is allowed to. - */ - public static class Hostmaster extends IndependentRole { - - /** - * Hostmasters role to be assigned to users via via {@link User#setAuthorities}. - */ - public static final Hostmaster ROLE = Role.of(Hostmaster.class); - - Hostmaster() { - super(AuthoritiesConstants.HOSTMASTER, Admin.class); - } - } - - public static class Admin extends IndependentRole { - - public static final Admin ROLE = Role.of(Admin.class); - - Admin() { - super(AuthoritiesConstants.ADMIN, Supporter.class); - } - } - - public static class Supporter extends IndependentRole { - - public static final Supporter ROLE = Role.of(Supporter.class); - - Supporter() { - super(AuthoritiesConstants.SUPPORTER, CustomerContractualContact.class); - } - } - - /** - * This role is for contractual contacts of a customer, like a director of the company. - *

- * Who has this role, has the broadest access to all resources which belong to this customer. - * Everything which relates to the contract with the customer, needs this role. - *

- * This role can be assigned to a user via {@link UserRoleAssignment}. - *

- */ - public static class CustomerContractualContact extends DependentRole { - - public static final CustomerContractualContact ROLE = Role.of(CustomerContractualContact.class); - - CustomerContractualContact() { - super(CustomerFinancialContact.class, CustomerTechnicalContact.class); - } - } - - public static class CustomerFinancialContact extends DependentRole { - - public static final CustomerFinancialContact ROLE = Role.of(CustomerFinancialContact.class); - - CustomerFinancialContact() { - super(AnyCustomerContact.class); - } - } - - public static class CustomerTechnicalContact extends DependentRole { - - public static final CustomerTechnicalContact ROLE = Role.of(CustomerTechnicalContact.class); - - CustomerTechnicalContact() { - super( - AnyCustomerContact.class, - AnyCustomerUser.class); // TODO mhoennig: how to add roles of other modules? - } - } - - public static class AnyCustomerContact extends DependentRole { - - public static final AnyCustomerContact ROLE = Role.of(AnyCustomerContact.class); - - AnyCustomerContact() { - super(Anybody.class); - } - } - - public static class ActualCustomerUser extends DependentRole { - - public static final ActualCustomerUser ROLE = Role.of(ActualCustomerUser.class); - - ActualCustomerUser() { - super(AnyCustomerUser.class); - } - } - - public static class AnyCustomerUser extends IndependentRole { - - public static final Role ROLE = Role.of(AnyCustomerUser.class); - - AnyCustomerUser() { - super(AuthoritiesConstants.USER, Anybody.class); - } - } - - /** - * This role is meant to specify that a resources can be accessed by anybody, even without login. - *

- * It can be used to specify to grant rights to any use, even if unauthorized. - *

- */ - public static class Anybody extends IndependentRole { - - public static final Role ROLE = Role.of(Anybody.class); - - Anybody() { - super(AuthoritiesConstants.ANONYMOUS); - } - } - - /** - * Pseudo-role to mark init/update access as ignored because the field is display-only. - *

- * This allows REST clients to send the whole response back as a new update request. - * This role is not covered by any and covers itself no role. - *

- * It's only used to ignore the field. - *

- */ - public static class Ignored extends DependentRole { - - public static final Role ROLE = Role.of(Ignored.class); - } - - /** - * @param field a field of a DTO with AccessMappings - * @return true if update access can be ignored because the field is just for display anyway - */ - public static boolean toBeIgnoredForUpdates(final Field field) { - final AccessFor accessForAnnot = field.getAnnotation(AccessFor.class); - if (accessForAnnot == null) { - return true; - } - final Class[] updateAccessFor = field.getAnnotation(AccessFor.class).update(); - return updateAccessFor.length == 1 && updateAccessFor[0] == Ignored.class; - } - - /** - * @return the independent authority related 1:1 to this Role or empty if no independent authority is related 1:1 - * @see AuthoritiesConstants - */ - public String authority() { - return authority; - } - - /** - * @return the role with the broadest access rights - */ - public static Role broadest(final Role role, final Role... roles) { - Role broadests = role; - for (Role r : roles) { - if (r.covers(broadests.getClass())) { - broadests = r; - } - } - return broadests; - } - - /** - * Determines if 'this' actual role covered the given required role. - *

- * Where 'this' means the Java instance itself as a role of a system user. - *

- * {@code - * AssignedHostmaster.ROLE.covers(AssignedRole.ANY_CUSTOMER_USER) == true - * } - * - * @param roleClass The required role for a resource. - * @return whether this role comprises the given role - */ - public boolean covers(final Class roleClass) { - if (getClass() == Ignored.class || roleClass == Ignored.class) { - return false; - } - if (getClass() == roleClass) { - return true; - } - for (Role role : comprises.get()) { - if (role.covers(roleClass)) { - return true; - } - } - return false; - } - - /** - * Determines if 'this' actual role covers any of the given required roles. - *

- * Where 'this' means the Java instance itself as a role of a system user. - *

- * {@code - * AssignedHostmaster.ROLE.coversAny(AssignedRole.CUSTOMER_CONTRACTUAL_CONTACT, AssignedRole.CUSTOMER_FINANCIAL_CONTACT) == true - * } - * - * @param roleClasses The alternatively required roles for a resource. Must be at least one. - * @return whether this role comprises any of the given roles - */ - public boolean coversAny(final Class... roleClasses) { - verify(roleClasses != null && roleClasses.length > 0, "role classes expected"); - - for (Class roleClass : roleClasses) { - if (this.covers(roleClass)) { - return true; - } - } - return false; - } - - /** - * Checks if this role of a user allows to initialize the given field when creating the resource. - * - * @param field a field of the DTO of a resource - * @return true if allowed - */ - public boolean isAllowedToInit(final Field field) { - - final AccessFor accessFor = field.getAnnotation(AccessFor.class); - if (accessFor == null) { - return false; - } - - return coversAny(accessFor.init()); - } - - /** - * Checks if this role of a user allows to update the given field. - * - * @param field a field of the DTO of a resource - * @return true if allowed - */ - public boolean isAllowedToUpdate(final Field field) { - - final AccessFor accessFor = field.getAnnotation(AccessFor.class); - if (accessFor == null) { - return false; - } - - return coversAny(accessFor.update()); - } - - /** - * Checks if this role of a user allows to read the given field. - * - * @param field a field of the DTO of a resource - * @return true if allowed - */ - public boolean isAllowedToRead(final Field field) { - - final AccessFor accessFor = field.getAnnotation(AccessFor.class); - if (accessFor == null) { - return false; - } - - return coversAny(accessFor.read()); - } -} - -class LazyRoles { - - private final Class[] comprisedRoleClasses; - private Role[] comprisedRoles = null; - - LazyRoles(Class... comprisedRoleClasses) { - this.comprisedRoleClasses = comprisedRoleClasses; - } - - Role[] get() { - if (comprisedRoles == null) { - comprisedRoles = new Role[comprisedRoleClasses.length]; - for (int n = 0; n < comprisedRoleClasses.length; ++n) { - comprisedRoles[n] = Role.of(comprisedRoleClasses[n]); - } - } - return comprisedRoles; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/SelfId.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/SelfId.java deleted file mode 100644 index 35725a9b..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/SelfId.java +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import org.hostsharing.hsadminng.service.IdToDtoResolver; - -import java.lang.annotation.*; - -/** - * Used to mark a field within a DTO as containing the own id, - * it's needed to identify an existing entity for update functions. - * Initialization and update rights have no meaning for such fields, - * its initialized automatically and never updated. - * - * @see AccessFor - */ -@Documented -@Target({ ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface SelfId { - - /// The service which can load the referenced DTO. - Class> resolver(); -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/AssetCriteria.java b/src/main/java/org/hostsharing/hsadminng/service/dto/AssetCriteria.java deleted file mode 100644 index 0fe2ed74..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/AssetCriteria.java +++ /dev/null @@ -1,146 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; - -import io.github.jhipster.service.filter.BigDecimalFilter; -import io.github.jhipster.service.filter.Filter; -import io.github.jhipster.service.filter.LocalDateFilter; -import io.github.jhipster.service.filter.LongFilter; -import io.github.jhipster.service.filter.StringFilter; - -import java.io.Serializable; -import java.util.Objects; - -/** - * Criteria class for the Asset entity. This class is used in AssetResource to - * receive all the possible filtering options from the Http GET request parameters. - * For example the following could be a valid requests: - * /assets?id.greaterThan=5&attr1.contains=something&attr2.specified=false - * As Spring is unable to properly convert the types, unless specific {@link Filter} class are used, we need to use - * fix type specific filters. - */ -public class AssetCriteria implements Serializable { - - /** - * Class for filtering AssetAction - */ - public static class AssetActionFilter extends Filter { - } - - private static final long serialVersionUID = 1L; - - private LongFilter id; - - private LocalDateFilter documentDate; - - private LocalDateFilter valueDate; - - private AssetActionFilter action; - - private BigDecimalFilter amount; - - private StringFilter remark; - - private LongFilter membershipId; - - public LongFilter getId() { - return id; - } - - public void setId(LongFilter id) { - this.id = id; - } - - public LocalDateFilter getDocumentDate() { - return documentDate; - } - - public void setDocumentDate(LocalDateFilter documentDate) { - this.documentDate = documentDate; - } - - public LocalDateFilter getValueDate() { - return valueDate; - } - - public void setValueDate(LocalDateFilter valueDate) { - this.valueDate = valueDate; - } - - public AssetActionFilter getAction() { - return action; - } - - public void setAction(AssetActionFilter action) { - this.action = action; - } - - public BigDecimalFilter getAmount() { - return amount; - } - - public void setAmount(BigDecimalFilter amount) { - this.amount = amount; - } - - public StringFilter getRemark() { - return remark; - } - - public void setRemark(StringFilter remark) { - this.remark = remark; - } - - public LongFilter getMembershipId() { - return membershipId; - } - - public void setMembershipId(LongFilter membershipId) { - this.membershipId = membershipId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final AssetCriteria that = (AssetCriteria) o; - return Objects.equals(id, that.id) && - Objects.equals(documentDate, that.documentDate) && - Objects.equals(valueDate, that.valueDate) && - Objects.equals(action, that.action) && - Objects.equals(amount, that.amount) && - Objects.equals(remark, that.remark) && - Objects.equals(membershipId, that.membershipId); - } - - @Override - public int hashCode() { - return Objects.hash( - id, - documentDate, - valueDate, - action, - amount, - remark, - membershipId); - } - - @Override - public String toString() { - return "AssetCriteria{" + - (id != null ? "id=" + id + ", " : "") + - (documentDate != null ? "documentDate=" + documentDate + ", " : "") + - (valueDate != null ? "valueDate=" + valueDate + ", " : "") + - (action != null ? "action=" + action + ", " : "") + - (amount != null ? "amount=" + amount + ", " : "") + - (remark != null ? "remark=" + remark + ", " : "") + - (membershipId != null ? "membershipId=" + membershipId + ", " : "") + - "}"; - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/AssetDTO.java b/src/main/java/org/hostsharing/hsadminng/service/dto/AssetDTO.java deleted file mode 100644 index bf88980d..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/AssetDTO.java +++ /dev/null @@ -1,174 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; -import org.hostsharing.hsadminng.service.AssetService; -import org.hostsharing.hsadminng.service.MembershipService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.*; -import org.hostsharing.hsadminng.service.accessfilter.Role.*; - -import org.springframework.boot.jackson.JsonComponent; -import org.springframework.context.ApplicationContext; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.Objects; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * A DTO for the Asset entity. - */ -@EntityTypeId(Asset.ENTITY_TYPE_ID) -public class AssetDTO implements Serializable, AccessMappings { - - @SelfId(resolver = AssetService.class) - @AccessFor(read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private Long id; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate documentDate; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate valueDate; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private AssetAction action; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private BigDecimal amount; - - @Size(max = 160) - @AccessFor(init = Admin.class, update = Admin.class, read = Supporter.class) - private String remark; - - @ParentId(resolver = MembershipService.class) - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private Long membershipId; - - @AccessFor(update = Ignored.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String membershipDisplayLabel; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public LocalDate getDocumentDate() { - return documentDate; - } - - public void setDocumentDate(LocalDate documentDate) { - this.documentDate = documentDate; - } - - public LocalDate getValueDate() { - return valueDate; - } - - public void setValueDate(LocalDate valueDate) { - this.valueDate = valueDate; - } - - public AssetAction getAction() { - return action; - } - - public void setAction(AssetAction action) { - this.action = action; - } - - public BigDecimal getAmount() { - return amount; - } - - public void setAmount(BigDecimal amount) { - this.amount = amount; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Long getMembershipId() { - return membershipId; - } - - public void setMembershipId(Long membershipId) { - this.membershipId = membershipId; - } - - public String getMembershipDisplayLabel() { - return membershipDisplayLabel; - } - - public void setMembershipDisplayLabel(String membershipDisplayLabel) { - this.membershipDisplayLabel = membershipDisplayLabel; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - AssetDTO assetDTO = (AssetDTO) o; - if (assetDTO.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), assetDTO.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "AssetDTO{" + - "id=" + getId() + - ", documentDate='" + getDocumentDate() + "'" + - ", valueDate='" + getValueDate() + "'" + - ", action='" + getAction() + "'" + - ", amount=" + getAmount() + - ", remark='" + getRemark() + "'" + - ", membership=" + getMembershipId() + - ", membershipDisplayLabel='" + getMembershipDisplayLabel() + "'" + - "}"; - } - - @JsonComponent - public static class JsonSerializer extends JsonSerializerWithAccessFilter { - - public JsonSerializer(final ApplicationContext ctx, final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } - - @JsonComponent - public static class JsonDeserializer extends JsonDeserializerWithAccessFilter { - - public JsonDeserializer(final ApplicationContext ctx, final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerCriteria.java b/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerCriteria.java deleted file mode 100644 index 4638eaa8..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerCriteria.java +++ /dev/null @@ -1,292 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.domain.enumeration.CustomerKind; -import org.hostsharing.hsadminng.domain.enumeration.VatRegion; - -import io.github.jhipster.service.filter.*; - -import java.io.Serializable; -import java.util.Objects; - -/** - * Criteria class for the Customer entity. This class is used in CustomerResource to - * receive all the possible filtering options from the Http GET request parameters. - * For example the following could be a valid requests: - * /customers?id.greaterThan=5&attr1.contains=something&attr2.specified=false - * As Spring is unable to properly convert the types, unless specific {@link Filter} class are used, we need to use - * fix type specific filters. - */ -public class CustomerCriteria implements Serializable { - - /** - * Class for filtering CustomerKind - */ - public static class CustomerKindFilter extends Filter { - } - - /** - * Class for filtering VatRegion - */ - public static class VatRegionFilter extends Filter { - } - - private static final long serialVersionUID = 1L; - - private LongFilter id; - - private IntegerFilter reference; - - private StringFilter prefix; - - private StringFilter name; - - private CustomerKindFilter kind; - - private LocalDateFilter birthDate; - - private StringFilter birthPlace; - - private StringFilter registrationCourt; - - private StringFilter registrationNumber; - - private VatRegionFilter vatRegion; - - private StringFilter vatNumber; - - private StringFilter contractualSalutation; - - private StringFilter contractualAddress; - - private StringFilter billingSalutation; - - private StringFilter billingAddress; - - private StringFilter remark; - - private LongFilter membershipId; - - private LongFilter sepamandateId; - - public LongFilter getId() { - return id; - } - - public void setId(LongFilter id) { - this.id = id; - } - - public IntegerFilter getReference() { - return reference; - } - - public void setReference(IntegerFilter reference) { - this.reference = reference; - } - - public StringFilter getPrefix() { - return prefix; - } - - public void setPrefix(StringFilter prefix) { - this.prefix = prefix; - } - - public StringFilter getName() { - return name; - } - - public void setName(StringFilter name) { - this.name = name; - } - - public CustomerKindFilter getKind() { - return kind; - } - - public void setKind(CustomerKindFilter kind) { - this.kind = kind; - } - - public LocalDateFilter getBirthDate() { - return birthDate; - } - - public void setBirthDate(LocalDateFilter birthDate) { - this.birthDate = birthDate; - } - - public StringFilter getBirthPlace() { - return birthPlace; - } - - public void setBirthPlace(StringFilter birthPlace) { - this.birthPlace = birthPlace; - } - - public StringFilter getRegistrationCourt() { - return registrationCourt; - } - - public void setRegistrationCourt(StringFilter registrationCourt) { - this.registrationCourt = registrationCourt; - } - - public StringFilter getRegistrationNumber() { - return registrationNumber; - } - - public void setRegistrationNumber(StringFilter registrationNumber) { - this.registrationNumber = registrationNumber; - } - - public VatRegionFilter getVatRegion() { - return vatRegion; - } - - public void setVatRegion(VatRegionFilter vatRegion) { - this.vatRegion = vatRegion; - } - - public StringFilter getVatNumber() { - return vatNumber; - } - - public void setVatNumber(StringFilter vatNumber) { - this.vatNumber = vatNumber; - } - - public StringFilter getContractualSalutation() { - return contractualSalutation; - } - - public void setContractualSalutation(StringFilter contractualSalutation) { - this.contractualSalutation = contractualSalutation; - } - - public StringFilter getContractualAddress() { - return contractualAddress; - } - - public void setContractualAddress(StringFilter contractualAddress) { - this.contractualAddress = contractualAddress; - } - - public StringFilter getBillingSalutation() { - return billingSalutation; - } - - public void setBillingSalutation(StringFilter billingSalutation) { - this.billingSalutation = billingSalutation; - } - - public StringFilter getBillingAddress() { - return billingAddress; - } - - public void setBillingAddress(StringFilter billingAddress) { - this.billingAddress = billingAddress; - } - - public StringFilter getRemark() { - return remark; - } - - public void setRemark(StringFilter remark) { - this.remark = remark; - } - - public LongFilter getMembershipId() { - return membershipId; - } - - public void setMembershipId(LongFilter membershipId) { - this.membershipId = membershipId; - } - - public LongFilter getSepamandateId() { - return sepamandateId; - } - - public void setSepamandateId(LongFilter sepamandateId) { - this.sepamandateId = sepamandateId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final CustomerCriteria that = (CustomerCriteria) o; - return Objects.equals(id, that.id) && - Objects.equals(reference, that.reference) && - Objects.equals(prefix, that.prefix) && - Objects.equals(name, that.name) && - Objects.equals(kind, that.kind) && - Objects.equals(birthDate, that.birthDate) && - Objects.equals(birthPlace, that.birthPlace) && - Objects.equals(registrationCourt, that.registrationCourt) && - Objects.equals(registrationNumber, that.registrationNumber) && - Objects.equals(vatRegion, that.vatRegion) && - Objects.equals(vatNumber, that.vatNumber) && - Objects.equals(contractualSalutation, that.contractualSalutation) && - Objects.equals(contractualAddress, that.contractualAddress) && - Objects.equals(billingSalutation, that.billingSalutation) && - Objects.equals(billingAddress, that.billingAddress) && - Objects.equals(remark, that.remark) && - Objects.equals(membershipId, that.membershipId) && - Objects.equals(sepamandateId, that.sepamandateId); - } - - @Override - public int hashCode() { - return Objects.hash( - id, - reference, - prefix, - name, - kind, - birthDate, - birthPlace, - registrationCourt, - registrationNumber, - vatRegion, - vatNumber, - contractualSalutation, - contractualAddress, - billingSalutation, - billingAddress, - remark, - membershipId, - sepamandateId); - } - - @Override - public String toString() { - return "CustomerCriteria{" + - (id != null ? "id=" + id + ", " : "") + - (reference != null ? "reference=" + reference + ", " : "") + - (prefix != null ? "prefix=" + prefix + ", " : "") + - (name != null ? "name=" + name + ", " : "") + - (kind != null ? "kind=" + kind + ", " : "") + - (birthDate != null ? "birthDate=" + birthDate + ", " : "") + - (birthPlace != null ? "birthPlace=" + birthPlace + ", " : "") + - (registrationCourt != null ? "registrationCourt=" + registrationCourt + ", " : "") + - (registrationNumber != null ? "registrationNumber=" + registrationNumber + ", " : "") + - (vatRegion != null ? "vatRegion=" + vatRegion + ", " : "") + - (vatNumber != null ? "vatNumber=" + vatNumber + ", " : "") + - (contractualSalutation != null ? "contractualSalutation=" + contractualSalutation + ", " : "") + - (contractualAddress != null ? "contractualAddress=" + contractualAddress + ", " : "") + - (billingSalutation != null ? "billingSalutation=" + billingSalutation + ", " : "") + - (billingAddress != null ? "billingAddress=" + billingAddress + ", " : "") + - (remark != null ? "remark=" + remark + ", " : "") + - (membershipId != null ? "membershipId=" + membershipId + ", " : "") + - (sepamandateId != null ? "sepamandateId=" + sepamandateId + ", " : "") + - "}"; - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerDTO.java b/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerDTO.java deleted file mode 100644 index e4874407..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerDTO.java +++ /dev/null @@ -1,319 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import static org.hostsharing.hsadminng.service.accessfilter.Role.*; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.enumeration.CustomerKind; -import org.hostsharing.hsadminng.domain.enumeration.VatRegion; -import org.hostsharing.hsadminng.service.CustomerService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.*; - -import org.springframework.boot.jackson.JsonComponent; -import org.springframework.context.ApplicationContext; - -import java.time.LocalDate; -import java.util.Objects; - -import javax.validation.constraints.*; - -/** - * A DTO for the Customer entity. - */ -@EntityTypeId(Customer.ENTITY_TYPE_ID) -public class CustomerDTO implements AccessMappings, FluentBuilder { - - @SelfId(resolver = CustomerService.class) - @AccessFor(read = AnyCustomerUser.class) - private Long id; - - @NotNull - @Min(value = 10000) - @Max(value = 99999) - @AccessFor(init = Admin.class, read = AnyCustomerUser.class) - private Integer reference; - - @NotNull - @Size(max = 3) - @Pattern(regexp = "[a-z][a-z0-9]+") - @AccessFor(init = Admin.class, read = AnyCustomerUser.class) - private String prefix; - - @NotNull - @Size(max = 80) - @AccessFor(init = Admin.class, update = Admin.class, read = AnyCustomerUser.class) - private String name; - - @NotNull - @AccessFor(init = Admin.class, update = Admin.class, read = CustomerContractualContact.class) - private CustomerKind kind; - - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate birthDate; - - @Size(max = 80) - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String birthPlace; - - @Size(max = 80) - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String registrationCourt; - - @Size(max = 80) - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String registrationNumber; - - @NotNull - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private VatRegion vatRegion; - - @Size(max = 40) - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String vatNumber; - - @Size(max = 80) - @AccessFor(init = Admin.class, update = CustomerContractualContact.class, read = CustomerContractualContact.class) - private String contractualSalutation; - - @NotNull - @Size(max = 400) - @AccessFor(init = Admin.class, update = Admin.class, read = CustomerContractualContact.class) - private String contractualAddress; - - @Size(max = 80) - @AccessFor( - init = Admin.class, - update = { CustomerContractualContact.class, CustomerFinancialContact.class }, - read = CustomerContractualContact.class) - private String billingSalutation; - - @Size(max = 400) - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String billingAddress; - - @Size(max = 160) - @AccessFor(init = Admin.class, update = Supporter.class, read = Supporter.class) - private String remark; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = AnyCustomerUser.class) - private String displayLabel; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Integer getReference() { - return reference; - } - - public void setReference(Integer reference) { - this.reference = reference; - } - - public String getPrefix() { - return prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public CustomerKind getKind() { - return kind; - } - - public void setKind(CustomerKind kind) { - this.kind = kind; - } - - public LocalDate getBirthDate() { - return birthDate; - } - - public void setBirthDate(LocalDate birthDate) { - this.birthDate = birthDate; - } - - public String getBirthPlace() { - return birthPlace; - } - - public void setBirthPlace(String birthPlace) { - this.birthPlace = birthPlace; - } - - public String getRegistrationCourt() { - return registrationCourt; - } - - public void setRegistrationCourt(String registrationCourt) { - this.registrationCourt = registrationCourt; - } - - public String getRegistrationNumber() { - return registrationNumber; - } - - public void setRegistrationNumber(String registrationNumber) { - this.registrationNumber = registrationNumber; - } - - public VatRegion getVatRegion() { - return vatRegion; - } - - public void setVatRegion(VatRegion vatRegion) { - this.vatRegion = vatRegion; - } - - public String getVatNumber() { - return vatNumber; - } - - public void setVatNumber(String vatNumber) { - this.vatNumber = vatNumber; - } - - public String getContractualSalutation() { - return contractualSalutation; - } - - public void setContractualSalutation(String contractualSalutation) { - this.contractualSalutation = contractualSalutation; - } - - public String getContractualAddress() { - return contractualAddress; - } - - public void setContractualAddress(String contractualAddress) { - this.contractualAddress = contractualAddress; - } - - public String getBillingSalutation() { - return billingSalutation; - } - - public void setBillingSalutation(String billingSalutation) { - this.billingSalutation = billingSalutation; - } - - public String getBillingAddress() { - return billingAddress; - } - - public void setBillingAddress(String billingAddress) { - this.billingAddress = billingAddress; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public String getDisplayLabel() { - return displayLabel; - } - - public void setDisplayLabel(final String displayLabel) { - this.displayLabel = displayLabel; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - CustomerDTO customerDTO = (CustomerDTO) o; - if (customerDTO.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), customerDTO.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "CustomerDTO{" + - "id=" + getId() + - ", reference=" + getReference() + - ", prefix='" + getPrefix() + "'" + - ", name='" + getName() + "'" + - ", kind='" + getKind() + "'" + - ", birthDate='" + getBirthDate() + "'" + - ", birthPlace='" + getBirthPlace() + "'" + - ", registrationCourt='" + getRegistrationCourt() + "'" + - ", registrationNumber='" + getRegistrationNumber() + "'" + - ", vatRegion='" + getVatRegion() + "'" + - ", vatNumber='" + getVatNumber() + "'" + - ", contractualSalutation='" + getContractualSalutation() + "'" + - ", contractualAddress='" + getContractualAddress() + "'" + - ", billingSalutation='" + getBillingSalutation() + "'" + - ", billingAddress='" + getBillingAddress() + "'" + - ", remark='" + getRemark() + "'" + - "}"; - } - - @JsonComponent - public static class CustomerJsonSerializer extends JsonSerializerWithAccessFilter { - - public CustomerJsonSerializer(final ApplicationContext ctx, final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } - - @JsonComponent - public static class CustomerJsonDeserializer extends JsonDeserializerWithAccessFilter { - - public CustomerJsonDeserializer( - final ApplicationContext ctx, - final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/FluentBuilder.java b/src/main/java/org/hostsharing/hsadminng/service/dto/FluentBuilder.java deleted file mode 100644 index 55b6e87e..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/FluentBuilder.java +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import java.util.function.Consumer; - -/** - * Just 'implement' this interface in your class to get a pseudo fluent builder, no more code needed. - * - * @param class to be build (same as to which the interface was added) - */ -public interface FluentBuilder { - - /** - * Allows statements on the target instance possible as expression. - * - * This allows creating nested object structures, e.g. for test data - * in a much more readable way. - * - *

Example

- * {code - * // adding a fluent builder to your class - * class YourClass implements FluentBuilder { - * public int someField; - * public String anotherField; - * // ... - * } - * - * // using the fluent builder somewhere else - * someMethod( new YourClass().with( it -> { - * it.someField = 5; - * it.anotherField = "Hello"; - * })); - * } - * - * @param builderFunction statements to apply to 'this' - * - * @return the instance on which 'with(...)' was executed. - */ - @SuppressWarnings("unchecked") - default T with( - Consumer builderFunction) { - builderFunction.accept((T) this); - return (T) this; - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/MembershipCriteria.java b/src/main/java/org/hostsharing/hsadminng/service/dto/MembershipCriteria.java deleted file mode 100644 index c8ec2bf8..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/MembershipCriteria.java +++ /dev/null @@ -1,163 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import io.github.jhipster.service.filter.Filter; -import io.github.jhipster.service.filter.LocalDateFilter; -import io.github.jhipster.service.filter.LongFilter; -import io.github.jhipster.service.filter.StringFilter; - -import java.io.Serializable; -import java.util.Objects; - -/** - * Criteria class for the Membership entity. This class is used in MembershipResource to - * receive all the possible filtering options from the Http GET request parameters. - * For example the following could be a valid requests: - * /memberships?id.greaterThan=5&attr1.contains=something&attr2.specified=false - * As Spring is unable to properly convert the types, unless specific {@link Filter} class are used, we need to use - * fix type specific filters. - */ -public class MembershipCriteria implements Serializable { - - private static final long serialVersionUID = 1L; - - private LongFilter id; - - private LocalDateFilter admissionDocumentDate; - - private LocalDateFilter cancellationDocumentDate; - - private LocalDateFilter memberFromDate; - - private LocalDateFilter memberUntilDate; - - private StringFilter remark; - - private LongFilter shareId; - - private LongFilter assetId; - - private LongFilter customerId; - - public LongFilter getId() { - return id; - } - - public void setId(LongFilter id) { - this.id = id; - } - - public LocalDateFilter getAdmissionDocumentDate() { - return admissionDocumentDate; - } - - public void setAdmissionDocumentDate(LocalDateFilter admissionDocumentDate) { - this.admissionDocumentDate = admissionDocumentDate; - } - - public LocalDateFilter getCancellationDocumentDate() { - return cancellationDocumentDate; - } - - public void setCancellationDocumentDate(LocalDateFilter cancellationDocumentDate) { - this.cancellationDocumentDate = cancellationDocumentDate; - } - - public LocalDateFilter getMemberFromDate() { - return memberFromDate; - } - - public void setMemberFromDate(LocalDateFilter memberFromDate) { - this.memberFromDate = memberFromDate; - } - - public LocalDateFilter getMemberUntilDate() { - return memberUntilDate; - } - - public void setMemberUntilDate(LocalDateFilter memberUntilDate) { - this.memberUntilDate = memberUntilDate; - } - - public StringFilter getRemark() { - return remark; - } - - public void setRemark(StringFilter remark) { - this.remark = remark; - } - - public LongFilter getShareId() { - return shareId; - } - - public void setShareId(LongFilter shareId) { - this.shareId = shareId; - } - - public LongFilter getAssetId() { - return assetId; - } - - public void setAssetId(LongFilter assetId) { - this.assetId = assetId; - } - - public LongFilter getCustomerId() { - return customerId; - } - - public void setCustomerId(LongFilter customerId) { - this.customerId = customerId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final MembershipCriteria that = (MembershipCriteria) o; - return Objects.equals(id, that.id) && - Objects.equals(admissionDocumentDate, that.admissionDocumentDate) && - Objects.equals(cancellationDocumentDate, that.cancellationDocumentDate) && - Objects.equals(memberFromDate, that.memberFromDate) && - Objects.equals(memberUntilDate, that.memberUntilDate) && - Objects.equals(remark, that.remark) && - Objects.equals(shareId, that.shareId) && - Objects.equals(assetId, that.assetId) && - Objects.equals(customerId, that.customerId); - } - - @Override - public int hashCode() { - return Objects.hash( - id, - admissionDocumentDate, - cancellationDocumentDate, - memberFromDate, - memberUntilDate, - remark, - shareId, - assetId, - customerId); - } - - @Override - public String toString() { - return "MembershipCriteria{" + - (id != null ? "id=" + id + ", " : "") + - (admissionDocumentDate != null ? "admissionDocumentDate=" + admissionDocumentDate + ", " : "") + - (cancellationDocumentDate != null ? "cancellationDocumentDate=" + cancellationDocumentDate + ", " : "") + - (memberFromDate != null ? "memberFromDate=" + memberFromDate + ", " : "") + - (memberUntilDate != null ? "memberUntilDate=" + memberUntilDate + ", " : "") + - (remark != null ? "remark=" + remark + ", " : "") + - (shareId != null ? "shareId=" + shareId + ", " : "") + - (assetId != null ? "assetId=" + assetId + ", " : "") + - (customerId != null ? "customerId=" + customerId + ", " : "") + - "}"; - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/MembershipDTO.java b/src/main/java/org/hostsharing/hsadminng/service/dto/MembershipDTO.java deleted file mode 100644 index cbe93fc5..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/MembershipDTO.java +++ /dev/null @@ -1,203 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.service.CustomerService; -import org.hostsharing.hsadminng.service.MembershipService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.*; -import org.hostsharing.hsadminng.service.accessfilter.Role.*; - -import org.springframework.boot.jackson.JsonComponent; -import org.springframework.context.ApplicationContext; - -import java.time.LocalDate; -import java.util.Objects; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * A DTO for the Membership entity. - */ -@EntityTypeId(Membership.ENTITY_TYPE_ID) -public class MembershipDTO implements AccessMappings, FluentBuilder { - - @SelfId(resolver = MembershipService.class) - @AccessFor(read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private Long id; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate admissionDocumentDate; - - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate cancellationDocumentDate; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate memberFromDate; - - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate memberUntilDate; - - @Size(max = 160) - @AccessFor(init = Admin.class, update = Admin.class, read = Supporter.class) - private String remark; - - @ParentId(resolver = CustomerService.class) - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private Long customerId; - - @AccessFor(update = Ignored.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String customerPrefix; - - @AccessFor(update = Ignored.class, read = CustomerFinancialContact.class) - private String customerDisplayLabel; - - @AccessFor(update = Ignored.class, read = CustomerFinancialContact.class) - private String displayLabel; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public LocalDate getAdmissionDocumentDate() { - return admissionDocumentDate; - } - - public void setAdmissionDocumentDate(LocalDate admissionDocumentDate) { - this.admissionDocumentDate = admissionDocumentDate; - } - - public LocalDate getCancellationDocumentDate() { - return cancellationDocumentDate; - } - - public void setCancellationDocumentDate(LocalDate cancellationDocumentDate) { - this.cancellationDocumentDate = cancellationDocumentDate; - } - - public LocalDate getMemberFromDate() { - return memberFromDate; - } - - public void setMemberFromDate(LocalDate memberFromDate) { - this.memberFromDate = memberFromDate; - } - - public LocalDate getMemberUntilDate() { - return memberUntilDate; - } - - public void setMemberUntilDate(LocalDate memberUntilDate) { - this.memberUntilDate = memberUntilDate; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Long getCustomerId() { - return customerId; - } - - public void setCustomerId(Long customerId) { - this.customerId = customerId; - } - - public String getCustomerPrefix() { - return customerPrefix; - } - - public void setCustomerPrefix(String customerPrefix) { - this.customerPrefix = customerPrefix; - } - - public String getCustomerDisplayLabel() { - return customerDisplayLabel; - } - - public void setCustomerDisplayLabel(final String customerDisplayLabel) { - this.customerDisplayLabel = customerDisplayLabel; - } - - public String getDisplayLabel() { - return displayLabel; - } - - public void setDisplayLabel(final String displayLabel) { - this.displayLabel = displayLabel; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - MembershipDTO membershipDTO = (MembershipDTO) o; - if (membershipDTO.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), membershipDTO.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "MembershipDTO{" + - "id=" + getId() + - ", admissionDocumentDate='" + getAdmissionDocumentDate() + "'" + - ", cancellationDocumentDate='" + getCancellationDocumentDate() + "'" + - ", memberFromDate='" + getMemberFromDate() + "'" + - ", memberUntilDate='" + getMemberUntilDate() + "'" + - ", remark='" + getRemark() + "'" + - ", customer=" + getCustomerId() + - ", customerPrefix='" + getCustomerPrefix() + "'" + - ", customerDisplayLabel='" + getCustomerDisplayLabel() + "'" + - ", displayLabel='" + getDisplayLabel() + "'" + - "}"; - } - - @JsonComponent - public static class JsonSerializer extends JsonSerializerWithAccessFilter { - - public JsonSerializer( - final ApplicationContext ctx, - final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } - - @JsonComponent - public static class JsonDeserializer extends JsonDeserializerWithAccessFilter { - - public JsonDeserializer( - final ApplicationContext ctx, - final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/PasswordChangeDTO.java b/src/main/java/org/hostsharing/hsadminng/service/dto/PasswordChangeDTO.java deleted file mode 100644 index 356c1a1b..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/PasswordChangeDTO.java +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -/** - * A DTO representing a password change required data - current and new password. - */ -public class PasswordChangeDTO { - - private String currentPassword; - private String newPassword; - - public PasswordChangeDTO() { - // Empty constructor needed for Jackson. - } - - public PasswordChangeDTO(String currentPassword, String newPassword) { - this.currentPassword = currentPassword; - this.newPassword = newPassword; - } - - public String getCurrentPassword() { - - return currentPassword; - } - - public void setCurrentPassword(String currentPassword) { - this.currentPassword = currentPassword; - } - - public String getNewPassword() { - return newPassword; - } - - public void setNewPassword(String newPassword) { - this.newPassword = newPassword; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/SepaMandateCriteria.java b/src/main/java/org/hostsharing/hsadminng/service/dto/SepaMandateCriteria.java deleted file mode 100644 index 39488003..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/SepaMandateCriteria.java +++ /dev/null @@ -1,189 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import io.github.jhipster.service.filter.Filter; -import io.github.jhipster.service.filter.LocalDateFilter; -import io.github.jhipster.service.filter.LongFilter; -import io.github.jhipster.service.filter.StringFilter; - -import java.io.Serializable; -import java.util.Objects; - -/** - * Criteria class for the SepaMandate entity. This class is used in SepaMandateResource to - * receive all the possible filtering options from the Http GET request parameters. - * For example the following could be a valid requests: - * /sepa-mandates?id.greaterThan=5&attr1.contains=something&attr2.specified=false - * As Spring is unable to properly convert the types, unless specific {@link Filter} class are used, we need to use - * fix type specific filters. - */ -public class SepaMandateCriteria implements Serializable { - - private static final long serialVersionUID = 1L; - - private LongFilter id; - - private StringFilter reference; - - private StringFilter iban; - - private StringFilter bic; - - private LocalDateFilter grantingDocumentDate; - - private LocalDateFilter revokationDocumentDate; - - private LocalDateFilter validFromDate; - - private LocalDateFilter validUntilDate; - - private LocalDateFilter lastUsedDate; - - private StringFilter remark; - - private LongFilter customerId; - - public LongFilter getId() { - return id; - } - - public void setId(LongFilter id) { - this.id = id; - } - - public StringFilter getReference() { - return reference; - } - - public void setReference(StringFilter reference) { - this.reference = reference; - } - - public StringFilter getIban() { - return iban; - } - - public void setIban(StringFilter iban) { - this.iban = iban; - } - - public StringFilter getBic() { - return bic; - } - - public void setBic(StringFilter bic) { - this.bic = bic; - } - - public LocalDateFilter getGrantingDocumentDate() { - return grantingDocumentDate; - } - - public void setGrantingDocumentDate(LocalDateFilter grantingDocumentDate) { - this.grantingDocumentDate = grantingDocumentDate; - } - - public LocalDateFilter getRevokationDocumentDate() { - return revokationDocumentDate; - } - - public void setRevokationDocumentDate(LocalDateFilter revokationDocumentDate) { - this.revokationDocumentDate = revokationDocumentDate; - } - - public LocalDateFilter getValidFromDate() { - return validFromDate; - } - - public void setValidFromDate(LocalDateFilter validFromDate) { - this.validFromDate = validFromDate; - } - - public LocalDateFilter getValidUntilDate() { - return validUntilDate; - } - - public void setValidUntilDate(LocalDateFilter validUntilDate) { - this.validUntilDate = validUntilDate; - } - - public LocalDateFilter getLastUsedDate() { - return lastUsedDate; - } - - public void setLastUsedDate(LocalDateFilter lastUsedDate) { - this.lastUsedDate = lastUsedDate; - } - - public StringFilter getRemark() { - return remark; - } - - public void setRemark(StringFilter remark) { - this.remark = remark; - } - - public LongFilter getCustomerId() { - return customerId; - } - - public void setCustomerId(LongFilter customerId) { - this.customerId = customerId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final SepaMandateCriteria that = (SepaMandateCriteria) o; - return Objects.equals(id, that.id) && - Objects.equals(reference, that.reference) && - Objects.equals(iban, that.iban) && - Objects.equals(bic, that.bic) && - Objects.equals(grantingDocumentDate, that.grantingDocumentDate) && - Objects.equals(revokationDocumentDate, that.revokationDocumentDate) && - Objects.equals(validFromDate, that.validFromDate) && - Objects.equals(validUntilDate, that.validUntilDate) && - Objects.equals(lastUsedDate, that.lastUsedDate) && - Objects.equals(remark, that.remark) && - Objects.equals(customerId, that.customerId); - } - - @Override - public int hashCode() { - return Objects.hash( - id, - reference, - iban, - bic, - grantingDocumentDate, - revokationDocumentDate, - validFromDate, - validUntilDate, - lastUsedDate, - remark, - customerId); - } - - @Override - public String toString() { - return "SepaMandateCriteria{" + - (id != null ? "id=" + id + ", " : "") + - (reference != null ? "reference=" + reference + ", " : "") + - (iban != null ? "iban=" + iban + ", " : "") + - (bic != null ? "bic=" + bic + ", " : "") + - (grantingDocumentDate != null ? "grantingDocumentDate=" + grantingDocumentDate + ", " : "") + - (revokationDocumentDate != null ? "revokationDocumentDate=" + revokationDocumentDate + ", " : "") + - (validFromDate != null ? "validFromDate=" + validFromDate + ", " : "") + - (validUntilDate != null ? "validUntilDate=" + validUntilDate + ", " : "") + - (lastUsedDate != null ? "lastUsedDate=" + lastUsedDate + ", " : "") + - (remark != null ? "remark=" + remark + ", " : "") + - (customerId != null ? "customerId=" + customerId + ", " : "") + - "}"; - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTO.java b/src/main/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTO.java deleted file mode 100644 index 1250b4d8..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTO.java +++ /dev/null @@ -1,242 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.domain.SepaMandate; -import org.hostsharing.hsadminng.service.CustomerService; -import org.hostsharing.hsadminng.service.SepaMandateService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.*; -import org.hostsharing.hsadminng.service.accessfilter.Role.*; - -import org.springframework.boot.jackson.JsonComponent; -import org.springframework.context.ApplicationContext; - -import java.time.LocalDate; -import java.util.Objects; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * A DTO for the SepaMandate entity. - */ -@EntityTypeId(SepaMandate.ENTITY_TYPE_ID) -public class SepaMandateDTO implements AccessMappings, FluentBuilder { - - @SelfId(resolver = SepaMandateService.class) - @AccessFor(read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private Long id; - - @NotNull - @Size(max = 40) - @AccessFor( - init = { CustomerContractualContact.class, CustomerFinancialContact.class }, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String reference; - - @Size(max = 34) - @AccessFor( - init = { CustomerContractualContact.class, CustomerFinancialContact.class }, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String iban; - - @Size(max = 11) - @AccessFor( - init = { CustomerContractualContact.class, CustomerFinancialContact.class }, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String bic; - - @NotNull - @AccessFor( - init = { CustomerContractualContact.class, CustomerFinancialContact.class }, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate grantingDocumentDate; - - @AccessFor( - init = Admin.class, - update = { CustomerContractualContact.class, CustomerFinancialContact.class }, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate revokationDocumentDate; - - @NotNull - @AccessFor( - init = { CustomerContractualContact.class, CustomerFinancialContact.class }, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate validFromDate; - - @AccessFor( - init = { CustomerContractualContact.class, CustomerFinancialContact.class }, - update = { CustomerContractualContact.class, CustomerFinancialContact.class }, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate validUntilDate; - - @AccessFor( - init = Admin.class, - update = Admin.class, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate lastUsedDate; - - @Size(max = 160) - @AccessFor(init = Admin.class, update = Supporter.class, read = Supporter.class) - private String remark; - - @ParentId(resolver = CustomerService.class) - @AccessFor( - init = { CustomerContractualContact.class, CustomerFinancialContact.class }, - read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private Long customerId; - - @AccessFor(update = Ignored.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String customerDisplayLabel; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getReference() { - return reference; - } - - public void setReference(String reference) { - this.reference = reference; - } - - public String getIban() { - return iban; - } - - public void setIban(String iban) { - this.iban = iban; - } - - public String getBic() { - return bic; - } - - public void setBic(String bic) { - this.bic = bic; - } - - public LocalDate getGrantingDocumentDate() { - return grantingDocumentDate; - } - - public void setGrantingDocumentDate(LocalDate grantingDocumentDate) { - this.grantingDocumentDate = grantingDocumentDate; - } - - public LocalDate getRevokationDocumentDate() { - return revokationDocumentDate; - } - - public void setRevokationDocumentDate(LocalDate revokationDocumentDate) { - this.revokationDocumentDate = revokationDocumentDate; - } - - public LocalDate getValidFromDate() { - return validFromDate; - } - - public void setValidFromDate(LocalDate validFromDate) { - this.validFromDate = validFromDate; - } - - public LocalDate getValidUntilDate() { - return validUntilDate; - } - - public void setValidUntilDate(LocalDate validUntilDate) { - this.validUntilDate = validUntilDate; - } - - public LocalDate getLastUsedDate() { - return lastUsedDate; - } - - public void setLastUsedDate(LocalDate lastUsedDate) { - this.lastUsedDate = lastUsedDate; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Long getCustomerId() { - return customerId; - } - - public void setCustomerId(Long customerId) { - this.customerId = customerId; - } - - public String getCustomerDisplayLabel() { - return customerDisplayLabel; - } - - public void setCustomerDisplayLabel(String customerDisplayLabel) { - this.customerDisplayLabel = customerDisplayLabel; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - SepaMandateDTO sepaMandateDTO = (SepaMandateDTO) o; - if (sepaMandateDTO.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), sepaMandateDTO.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "SepaMandateDTO{" + - "id=" + getId() + - ", reference='" + getReference() + "'" + - ", iban='" + getIban() + "'" + - ", bic='" + getBic() + "'" + - ", grantingDocumentDate='" + getGrantingDocumentDate() + "'" + - ", revokationDocumentDate='" + getRevokationDocumentDate() + "'" + - ", validFromDate='" + getValidFromDate() + "'" + - ", validUntilDate='" + getValidUntilDate() + "'" + - ", lastUsedDate='" + getLastUsedDate() + "'" + - ", remark='" + getRemark() + "'" + - ", customer=" + getCustomerId() + - ", customerDisplayLabel='" + getCustomerDisplayLabel() + "'" + - "}"; - } - - @JsonComponent - public static class JsonSerializer extends JsonSerializerWithAccessFilter { - - public JsonSerializer(final ApplicationContext ctx, final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } - - @JsonComponent - public static class JsonDeserializer extends JsonDeserializerWithAccessFilter { - - public JsonDeserializer(final ApplicationContext ctx, final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/ShareCriteria.java b/src/main/java/org/hostsharing/hsadminng/service/dto/ShareCriteria.java deleted file mode 100644 index e2fd56af..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/ShareCriteria.java +++ /dev/null @@ -1,146 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; - -import io.github.jhipster.service.filter.Filter; -import io.github.jhipster.service.filter.IntegerFilter; -import io.github.jhipster.service.filter.LocalDateFilter; -import io.github.jhipster.service.filter.LongFilter; -import io.github.jhipster.service.filter.StringFilter; - -import java.io.Serializable; -import java.util.Objects; - -/** - * Criteria class for the Share entity. This class is used in ShareResource to - * receive all the possible filtering options from the Http GET request parameters. - * For example the following could be a valid requests: - * /shares?id.greaterThan=5&attr1.contains=something&attr2.specified=false - * As Spring is unable to properly convert the types, unless specific {@link Filter} class are used, we need to use - * fix type specific filters. - */ -public class ShareCriteria implements Serializable { - - /** - * Class for filtering ShareAction - */ - public static class ShareActionFilter extends Filter { - } - - private static final long serialVersionUID = 1L; - - private LongFilter id; - - private LocalDateFilter documentDate; - - private LocalDateFilter valueDate; - - private ShareActionFilter action; - - private IntegerFilter quantity; - - private StringFilter remark; - - private LongFilter membershipId; - - public LongFilter getId() { - return id; - } - - public void setId(LongFilter id) { - this.id = id; - } - - public LocalDateFilter getDocumentDate() { - return documentDate; - } - - public void setDocumentDate(LocalDateFilter documentDate) { - this.documentDate = documentDate; - } - - public LocalDateFilter getValueDate() { - return valueDate; - } - - public void setValueDate(LocalDateFilter valueDate) { - this.valueDate = valueDate; - } - - public ShareActionFilter getAction() { - return action; - } - - public void setAction(ShareActionFilter action) { - this.action = action; - } - - public IntegerFilter getQuantity() { - return quantity; - } - - public void setQuantity(IntegerFilter quantity) { - this.quantity = quantity; - } - - public StringFilter getRemark() { - return remark; - } - - public void setRemark(StringFilter remark) { - this.remark = remark; - } - - public LongFilter getMembershipId() { - return membershipId; - } - - public void setMembershipId(LongFilter membershipId) { - this.membershipId = membershipId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final ShareCriteria that = (ShareCriteria) o; - return Objects.equals(id, that.id) && - Objects.equals(documentDate, that.documentDate) && - Objects.equals(valueDate, that.valueDate) && - Objects.equals(action, that.action) && - Objects.equals(quantity, that.quantity) && - Objects.equals(remark, that.remark) && - Objects.equals(membershipId, that.membershipId); - } - - @Override - public int hashCode() { - return Objects.hash( - id, - documentDate, - valueDate, - action, - quantity, - remark, - membershipId); - } - - @Override - public String toString() { - return "ShareCriteria{" + - (id != null ? "id=" + id + ", " : "") + - (documentDate != null ? "documentDate=" + documentDate + ", " : "") + - (valueDate != null ? "valueDate=" + valueDate + ", " : "") + - (action != null ? "action=" + action + ", " : "") + - (quantity != null ? "quantity=" + quantity + ", " : "") + - (remark != null ? "remark=" + remark + ", " : "") + - (membershipId != null ? "membershipId=" + membershipId + ", " : "") + - "}"; - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/ShareDTO.java b/src/main/java/org/hostsharing/hsadminng/service/dto/ShareDTO.java deleted file mode 100644 index 39193e90..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/ShareDTO.java +++ /dev/null @@ -1,173 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; -import org.hostsharing.hsadminng.service.MembershipService; -import org.hostsharing.hsadminng.service.ShareService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.*; -import org.hostsharing.hsadminng.service.accessfilter.Role.*; - -import org.springframework.boot.jackson.JsonComponent; -import org.springframework.context.ApplicationContext; - -import java.io.Serializable; -import java.time.LocalDate; -import java.util.Objects; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * A DTO for the Share entity. - */ -@EntityTypeId(Share.ENTITY_TYPE_ID) -public class ShareDTO implements Serializable, AccessMappings { - - @SelfId(resolver = ShareService.class) - @AccessFor(read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private Long id; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate documentDate; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private LocalDate valueDate; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private ShareAction action; - - @NotNull - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private Integer quantity; - - @Size(max = 160) - @AccessFor(init = Admin.class, update = Admin.class, read = Supporter.class) - private String remark; - - @ParentId(resolver = MembershipService.class) - @AccessFor(init = Admin.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private Long membershipId; - - @AccessFor(update = Ignored.class, read = { CustomerContractualContact.class, CustomerFinancialContact.class }) - private String membershipDisplayLabel; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public LocalDate getDocumentDate() { - return documentDate; - } - - public void setDocumentDate(LocalDate documentDate) { - this.documentDate = documentDate; - } - - public LocalDate getValueDate() { - return valueDate; - } - - public void setValueDate(LocalDate valueDate) { - this.valueDate = valueDate; - } - - public ShareAction getAction() { - return action; - } - - public void setAction(ShareAction action) { - this.action = action; - } - - public Integer getQuantity() { - return quantity; - } - - public void setQuantity(Integer quantity) { - this.quantity = quantity; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Long getMembershipId() { - return membershipId; - } - - public void setMembershipId(Long membershipId) { - this.membershipId = membershipId; - } - - public String getMembershipDisplayLabel() { - return membershipDisplayLabel; - } - - public void setMembershipDisplayLabel(String membershipDisplayLabel) { - this.membershipDisplayLabel = membershipDisplayLabel; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - ShareDTO shareDTO = (ShareDTO) o; - if (shareDTO.getId() == null || getId() == null) { - return false; - } - return Objects.equals(getId(), shareDTO.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "ShareDTO{" + - "id=" + getId() + - ", documentDate='" + getDocumentDate() + "'" + - ", valueDate='" + getValueDate() + "'" + - ", action='" + getAction() + "'" + - ", quantity=" + getQuantity() + - ", remark='" + getRemark() + "'" + - ", membership=" + getMembershipId() + - ", membershipDisplayLabel='" + getMembershipDisplayLabel() + "'" + - "}"; - } - - @JsonComponent - public static class JsonSerializer extends JsonSerializerWithAccessFilter { - - public JsonSerializer(final ApplicationContext ctx, final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } - - @JsonComponent - public static class JsonDeserializer extends JsonDeserializerWithAccessFilter { - - public JsonDeserializer(final ApplicationContext ctx, final UserRoleAssignmentService userRoleAssignmentService) { - super(ctx, userRoleAssignmentService); - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/UserDTO.java b/src/main/java/org/hostsharing/hsadminng/service/dto/UserDTO.java deleted file mode 100644 index fc9db9e0..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/UserDTO.java +++ /dev/null @@ -1,200 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.config.Constants; -import org.hostsharing.hsadminng.domain.Authority; -import org.hostsharing.hsadminng.domain.User; - -import java.time.Instant; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.validation.constraints.*; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotBlank; - -/** - * A DTO representing a user, with his authorities. - */ -public class UserDTO { - - private Long id; - - @NotBlank - @Pattern(regexp = Constants.LOGIN_REGEX) - @Size(min = 1, max = 50) - private String login; - - @Size(max = 50) - private String firstName; - - @Size(max = 50) - private String lastName; - - @Email - @Size(min = 5, max = 254) - private String email; - - @Size(max = 256) - private String imageUrl; - - private boolean activated = false; - - @Size(min = 2, max = 6) - private String langKey; - - private String createdBy; - - private Instant createdDate; - - private String lastModifiedBy; - - private Instant lastModifiedDate; - - private Set authorities; - - public UserDTO() { - // Empty constructor needed for Jackson. - } - - public UserDTO(User user) { - this.id = user.getId(); - this.login = user.getLogin(); - this.firstName = user.getFirstName(); - this.lastName = user.getLastName(); - this.email = user.getEmail(); - this.activated = user.getActivated(); - this.imageUrl = user.getImageUrl(); - this.langKey = user.getLangKey(); - this.createdBy = user.getCreatedBy(); - this.createdDate = user.getCreatedDate(); - this.lastModifiedBy = user.getLastModifiedBy(); - this.lastModifiedDate = user.getLastModifiedDate(); - this.authorities = user.getAuthorities() - .stream() - .map(Authority::getName) - .collect(Collectors.toSet()); - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getLogin() { - return login; - } - - public void setLogin(String login) { - this.login = login; - } - - 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 getImageUrl() { - return imageUrl; - } - - public void setImageUrl(String imageUrl) { - this.imageUrl = imageUrl; - } - - public boolean isActivated() { - return activated; - } - - public void setActivated(boolean activated) { - this.activated = activated; - } - - public String getLangKey() { - return langKey; - } - - public void setLangKey(String langKey) { - this.langKey = langKey; - } - - public String getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(String createdBy) { - this.createdBy = createdBy; - } - - public Instant getCreatedDate() { - return createdDate; - } - - public void setCreatedDate(Instant createdDate) { - this.createdDate = createdDate; - } - - public String getLastModifiedBy() { - return lastModifiedBy; - } - - public void setLastModifiedBy(String lastModifiedBy) { - this.lastModifiedBy = lastModifiedBy; - } - - public Instant getLastModifiedDate() { - return lastModifiedDate; - } - - public void setLastModifiedDate(Instant lastModifiedDate) { - this.lastModifiedDate = lastModifiedDate; - } - - public Set getAuthorities() { - return authorities; - } - - public void setAuthorities(Set authorities) { - this.authorities = authorities; - } - - @Override - public String toString() { - return "UserDTO{" + - "login='" + login + '\'' + - ", firstName='" + firstName + '\'' + - ", lastName='" + lastName + '\'' + - ", email='" + email + '\'' + - ", imageUrl='" + imageUrl + '\'' + - ", activated=" + activated + - ", langKey='" + langKey + '\'' + - ", createdBy=" + createdBy + - ", createdDate=" + createdDate + - ", lastModifiedBy='" + lastModifiedBy + '\'' + - ", lastModifiedDate=" + lastModifiedDate + - ", authorities=" + authorities + - "}"; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/UserRoleAssignmentCriteria.java b/src/main/java/org/hostsharing/hsadminng/service/dto/UserRoleAssignmentCriteria.java deleted file mode 100644 index e1d7f98e..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/UserRoleAssignmentCriteria.java +++ /dev/null @@ -1,110 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import io.github.jhipster.service.filter.Filter; -import io.github.jhipster.service.filter.LongFilter; -import io.github.jhipster.service.filter.StringFilter; - -import java.io.Serializable; -import java.util.Objects; - -/** - * Criteria class for the UserRoleAssignment entity. This class is used in UserRoleAssignmentResource to - * receive all the possible filtering options from the Http GET request parameters. - * For example the following could be a valid requests: - * /user-role-assignments?id.greaterThan=5&attr1.contains=something&attr2.specified=false - * As Spring is unable to properly convert the types, unless specific {@link Filter} class are used, we need to use - * fix type specific filters. - */ -public class UserRoleAssignmentCriteria implements Serializable { - - private static final long serialVersionUID = 1L; - - private LongFilter id; - - private StringFilter entityTypeId; - - private LongFilter entityObjectId; - - private StringFilter assignedRole; - - private LongFilter userId; - - public LongFilter getId() { - return id; - } - - public void setId(LongFilter id) { - this.id = id; - } - - public StringFilter getEntityTypeId() { - return entityTypeId; - } - - public void setEntityTypeId(StringFilter entityTypeId) { - this.entityTypeId = entityTypeId; - } - - public LongFilter getEntityObjectId() { - return entityObjectId; - } - - public void setEntityObjectId(LongFilter entityObjectId) { - this.entityObjectId = entityObjectId; - } - - public StringFilter getAssignedRole() { - return assignedRole; - } - - public void setAssignedRole(StringFilter assignedRole) { - this.assignedRole = assignedRole; - } - - public LongFilter getUserId() { - return userId; - } - - public void setUserId(LongFilter userId) { - this.userId = userId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final UserRoleAssignmentCriteria that = (UserRoleAssignmentCriteria) o; - return Objects.equals(id, that.id) && - Objects.equals(entityTypeId, that.entityTypeId) && - Objects.equals(entityObjectId, that.entityObjectId) && - Objects.equals(assignedRole, that.assignedRole) && - Objects.equals(userId, that.userId); - } - - @Override - public int hashCode() { - return Objects.hash( - id, - entityTypeId, - entityObjectId, - assignedRole, - userId); - } - - @Override - public String toString() { - return "UserRoleAssignmentCriteria{" + - (id != null ? "id=" + id + ", " : "") + - (entityTypeId != null ? "entityTypeId=" + entityTypeId + ", " : "") + - (entityObjectId != null ? "entityObjectId=" + entityObjectId + ", " : "") + - (assignedRole != null ? "assignedRole=" + assignedRole + ", " : "") + - (userId != null ? "userId=" + userId + ", " : "") + - "}"; - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/package-info.java b/src/main/java/org/hostsharing/hsadminng/service/dto/package-info.java deleted file mode 100644 index c680295c..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Data Transfer Objects. - */ -package org.hostsharing.hsadminng.service.dto; diff --git a/src/main/java/org/hostsharing/hsadminng/service/mapper/AssetMapper.java b/src/main/java/org/hostsharing/hsadminng/service/mapper/AssetMapper.java deleted file mode 100644 index 846405b5..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/mapper/AssetMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.mapper; - -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.service.dto.AssetDTO; - -import org.mapstruct.AfterMapping; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; - -/** - * Mapper for the entity Asset and its DTO AssetDTO. - */ -@Mapper(componentModel = "spring", uses = { MembershipMapper.class }) -public interface AssetMapper extends EntityMapper { - - @Mapping(source = "membership.id", target = "membershipId") - @Mapping(target = "membershipDisplayLabel", ignore = true) - AssetDTO toDto(Asset asset); - - @AfterMapping - default void setMembershipDisplayLabel(final @MappingTarget AssetDTO dto, final Asset entity) { - dto.setMembershipDisplayLabel(MembershipMapper.displayLabel(entity.getMembership())); - } - - @Mapping(source = "membershipId", target = "membership") - Asset toEntity(AssetDTO assetDTO); - - default Asset fromId(Long id) { - if (id == null) { - return null; - } - Asset asset = new Asset(); - asset.setId(id); - return asset; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/mapper/CustomerMapper.java b/src/main/java/org/hostsharing/hsadminng/service/mapper/CustomerMapper.java deleted file mode 100644 index 8664992c..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/mapper/CustomerMapper.java +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.mapper; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.service.dto.CustomerDTO; - -import org.mapstruct.AfterMapping; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; - -/** - * Mapper for the entity Customer and its DTO CustomerDTO. - */ -@Mapper(componentModel = "spring", uses = {}) -public interface CustomerMapper extends EntityMapper { - - static String displayLabel(Customer customer) { - return customer.getName() - + " [" + customer.getReference() + ":" + customer.getPrefix() + "]"; - } - - @Mapping(target = "displayLabel", ignore = true) - CustomerDTO toDto(Customer customer); - - @AfterMapping - default void setDisplayLabel(final @MappingTarget CustomerDTO dto, final Customer entity) { - dto.setDisplayLabel(displayLabel(entity)); - } - - @Mapping(target = "memberships", ignore = true) - @Mapping(target = "sepamandates", ignore = true) - Customer toEntity(CustomerDTO customerDTO); - - default Customer fromId(Long id) { - if (id == null) { - return null; - } - Customer customer = new Customer(); - customer.setId(id); - return customer; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/mapper/EntityMapper.java b/src/main/java/org/hostsharing/hsadminng/service/mapper/EntityMapper.java deleted file mode 100644 index 323e6639..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/mapper/EntityMapper.java +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.mapper; - -import java.util.List; - -/** - * Contract for a generic dto to entity mapper. - * - * @param - DTO type parameter. - * @param - Entity type parameter. - */ - -public interface EntityMapper { - - E toEntity(D dto); - - D toDto(E entity); - - List toEntity(List dtoList); - - List toDto(List entityList); -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/mapper/MembershipMapper.java b/src/main/java/org/hostsharing/hsadminng/service/mapper/MembershipMapper.java deleted file mode 100644 index 36f731f7..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/mapper/MembershipMapper.java +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.mapper; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.service.dto.MembershipDTO; - -import org.mapstruct.AfterMapping; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; - -import java.util.Objects; - -/** - * Mapper for the entity Membership and its DTO MembershipDTO. - */ -@Mapper(componentModel = "spring", uses = { CustomerMapper.class }) -public interface MembershipMapper extends EntityMapper { - - static String displayLabel(final Membership entity) { - final Customer customer = entity.getCustomer(); - return CustomerMapper.displayLabel(customer) + " " - + Objects.toString(entity.getMemberFromDate(), "") + " - " - + Objects.toString(entity.getMemberUntilDate(), "..."); - } - - @Mapping(source = "customer.id", target = "customerId") - @Mapping(source = "customer.prefix", target = "customerPrefix") - @Mapping(target = "displayLabel", ignore = true) - @Mapping(target = "customerDisplayLabel", ignore = true) - MembershipDTO toDto(Membership membership); - - // TODO BLOG HOWTO: multi-field display reference for selection lists - // also change the filed in the option list in *-update.html - @AfterMapping - default void setMembershipDisplayLabel(final @MappingTarget MembershipDTO dto, final Membership entity) { - dto.setDisplayLabel(displayLabel(entity)); - dto.setCustomerDisplayLabel(CustomerMapper.displayLabel(entity.getCustomer())); - } - - @Mapping(target = "shares", ignore = true) - @Mapping(target = "assets", ignore = true) - @Mapping(source = "customerId", target = "customer") - Membership toEntity(MembershipDTO membershipDTO); - - default Membership fromId(Long id) { - if (id == null) { - return null; - } - Membership membership = new Membership(); - membership.setId(id); - return membership; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/mapper/SepaMandateMapper.java b/src/main/java/org/hostsharing/hsadminng/service/mapper/SepaMandateMapper.java deleted file mode 100644 index d770d619..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/mapper/SepaMandateMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.mapper; - -import org.hostsharing.hsadminng.domain.SepaMandate; -import org.hostsharing.hsadminng.service.dto.SepaMandateDTO; - -import org.mapstruct.AfterMapping; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; - -/** - * Mapper for the entity SepaMandate and its DTO SepaMandateDTO. - */ -@Mapper(componentModel = "spring", uses = { CustomerMapper.class }) -public interface SepaMandateMapper extends EntityMapper { - - @Mapping(source = "customer.id", target = "customerId") - @Mapping(target = "customerDisplayLabel", ignore = true) - SepaMandateDTO toDto(SepaMandate sepaMandate); - - @AfterMapping - default void setDisplayLabels(final @MappingTarget SepaMandateDTO dto, final SepaMandate entity) { - dto.setCustomerDisplayLabel(CustomerMapper.displayLabel(entity.getCustomer())); - } - - @Mapping(source = "customerId", target = "customer") - SepaMandate toEntity(SepaMandateDTO sepaMandateDTO); - - default SepaMandate fromId(Long id) { - if (id == null) { - return null; - } - SepaMandate sepaMandate = new SepaMandate(); - sepaMandate.setId(id); - return sepaMandate; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/mapper/ShareMapper.java b/src/main/java/org/hostsharing/hsadminng/service/mapper/ShareMapper.java deleted file mode 100644 index 7ec02dca..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/mapper/ShareMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.mapper; - -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.service.dto.ShareDTO; - -import org.mapstruct.AfterMapping; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; - -/** - * Mapper for the entity Share and its DTO ShareDTO. - */ -@Mapper(componentModel = "spring", uses = { MembershipMapper.class }) -public interface ShareMapper extends EntityMapper { - - @Mapping(source = "membership.id", target = "membershipId") - @Mapping(target = "membershipDisplayLabel", ignore = true) - ShareDTO toDto(Share share); - - @AfterMapping - default void setMembershipDisplayLabel(final @MappingTarget ShareDTO dto, final Share entity) { - dto.setMembershipDisplayLabel(MembershipMapper.displayLabel(entity.getMembership())); - } - - @Mapping(source = "membershipId", target = "membership") - Share toEntity(ShareDTO shareDTO); - - default Share fromId(Long id) { - if (id == null) { - return null; - } - Share share = new Share(); - share.setId(id); - return share; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/mapper/UserMapper.java b/src/main/java/org/hostsharing/hsadminng/service/mapper/UserMapper.java deleted file mode 100644 index 5165f884..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/mapper/UserMapper.java +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.mapper; - -import org.hostsharing.hsadminng.domain.Authority; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.service.dto.UserDTO; - -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Mapper for the entity User and its DTO called UserDTO. - * - * Normal mappers are generated using MapStruct, this one is hand-coded as MapStruct - * support is still in beta, and requires a manual step with an IDE. - */ -@Service -public class UserMapper { - - public List usersToUserDTOs(List users) { - return users.stream() - .filter(Objects::nonNull) - .map(this::userToUserDTO) - .collect(Collectors.toList()); - } - - public UserDTO userToUserDTO(User user) { - return new UserDTO(user); - } - - public List userDTOsToUsers(List userDTOs) { - return userDTOs.stream() - .filter(Objects::nonNull) - .map(this::userDTOToUser) - .collect(Collectors.toList()); - } - - public User userDTOToUser(UserDTO userDTO) { - if (userDTO == null) { - return null; - } else { - User user = new User(); - user.setId(userDTO.getId()); - user.setLogin(userDTO.getLogin()); - user.setFirstName(userDTO.getFirstName()); - user.setLastName(userDTO.getLastName()); - user.setEmail(userDTO.getEmail()); - user.setImageUrl(userDTO.getImageUrl()); - user.setActivated(userDTO.isActivated()); - user.setLangKey(userDTO.getLangKey()); - Set authorities = this.authoritiesFromStrings(userDTO.getAuthorities()); - user.setAuthorities(authorities); - return user; - } - } - - private Set authoritiesFromStrings(Set authoritiesAsString) { - Set authorities = new HashSet<>(); - - if (authoritiesAsString != null) { - authorities = authoritiesAsString.stream().map(string -> { - Authority auth = new Authority(); - auth.setName(string); - return auth; - }).collect(Collectors.toSet()); - } - - return authorities; - } - - public User userFromId(Long id) { - if (id == null) { - return null; - } - User user = new User(); - user.setId(id); - return user; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/mapper/package-info.java b/src/main/java/org/hostsharing/hsadminng/service/mapper/package-info.java deleted file mode 100644 index 6231e7fe..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/mapper/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * MapStruct mappers for mapping domain objects and Data Transfer Objects. - */ -package org.hostsharing.hsadminng.service.mapper; diff --git a/src/main/java/org/hostsharing/hsadminng/service/package-info.java b/src/main/java/org/hostsharing/hsadminng/service/package-info.java deleted file mode 100644 index 1b8b3efb..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Service layer beans. - */ -package org.hostsharing.hsadminng.service; diff --git a/src/main/java/org/hostsharing/hsadminng/service/util/RandomUtil.java b/src/main/java/org/hostsharing/hsadminng/service/util/RandomUtil.java deleted file mode 100644 index c1f914a4..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/util/RandomUtil.java +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.util; - -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.RandomUtils; - -/** - * Utility class for generating random Values. - */ -public final class RandomUtil { - - private static final int DEF_COUNT = 20; - - private RandomUtil() { - } - - /** - * Generate a password. - * - * @return the generated password - */ - public static String generatePassword() { - return RandomStringUtils.randomAlphanumeric(DEF_COUNT); - } - - /** - * Generate an activation key. - * - * @return the generated activation key - */ - public static String generateActivationKey() { - return RandomStringUtils.randomNumeric(DEF_COUNT); - } - - /** - * Generate a reset key. - * - * @return the generated reset key - */ - public static String generateResetKey() { - return RandomStringUtils.randomNumeric(DEF_COUNT); - } - - /** - * Generate a random enum value for a given enum type. - * - * @return the generated enum value - */ - public static > E generateEnumValue(final Class enumType) { - final E[] enumValues = enumType.getEnumConstants(); - return enumValues[RandomUtils.nextInt(0, enumValues.length - 1)]; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/service/util/ReflectionUtil.java b/src/main/java/org/hostsharing/hsadminng/service/util/ReflectionUtil.java deleted file mode 100644 index 22e69417..00000000 --- a/src/main/java/org/hostsharing/hsadminng/service/util/ReflectionUtil.java +++ /dev/null @@ -1,218 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.util; - -import org.hostsharing.hsadminng.service.accessfilter.Role; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Optional; -import java.util.function.Function; - -public class ReflectionUtil { - - public static Field getField(final Class aClass, final String fieldName) { - try { - return aClass.getDeclaredField(fieldName); - } catch (final NoSuchFieldException e) { - if (aClass.getSuperclass() != Object.class) { - return getField(aClass.getSuperclass(), fieldName); - } - throw new IllegalArgumentException(e); - } - } - - public static void setValue(final T dto, final Field field, final Object value) { - try { - field.setAccessible(true); - field.set(dto, value); - } catch (final IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - @SuppressWarnings("unchecked") - public static R getValue(final T dto, final Field field) { - try { - field.setAccessible(true); - return (R) field.get(dto); - } catch (final IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - /** - * Searches the annotations of 'clazz' for an implemented interface 'rawInterface' and returns the class of the actual - * generics parameter at the specified index. - * - * @param clazz a class which implements the generic interface 'rawInterface' - * @param rawInterface a generic interface - * @param paramIndex the index of the generics parameter within 'rawInterface' - * @param the expected class of the generics parameter at position 'index' in 'rawInterface' - * @return the actual generics parameter - */ - public static Class determineGenericInterfaceParameter( - final Class clazz, - final Class rawInterface, - final int paramIndex) { - final Class found = determineGenericInterfaceParameterImpl(clazz, rawInterface, paramIndex); - if (found == null) { - throw new AssertionError( - clazz.getSimpleName() + " expected to implement " + rawInterface.getSimpleName() + "<...>"); - } - return found; - } - - @SuppressWarnings("unchecked") - private static Class determineGenericInterfaceParameterImpl( - final Class clazz, - final Class rawInterface, - final int paramIndex) { - for (Type genericInterface : clazz.getGenericInterfaces()) { - if (genericInterface instanceof ParameterizedType) { - final ParameterizedType parameterizedType = (ParameterizedType) genericInterface; - if (parameterizedType.getRawType() == rawInterface) { - return (Class) parameterizedType.getActualTypeArguments()[paramIndex]; - } - } - } - if (clazz.getSuperclass() != null) { - final Class found = determineGenericInterfaceParameterImpl(clazz.getSuperclass(), rawInterface, paramIndex); - if (found != null) { - return found; - } - } - for (Class implementedInterface : clazz.getInterfaces()) { - final Class found = determineGenericInterfaceParameterImpl(implementedInterface, rawInterface, paramIndex); - if (found != null) { - return found; - } - } - return null; - } - - /** - * Searches the annotations of 'clazz' for an extended class 'rawClass' and returns the class of the actual generics - * parameter at the specified index. - * - * @param clazz a class which implements the generic interface 'rawClass' - * @param rawClass a generic class - * @param paramIndex the index of the generics parameter within 'rawClass' - * @param the expected class of the generics parameter at position 'index' in 'rawClass' - * @return the actual generics parameter - */ - public static Class determineGenericClassParameter( - final Class clazz, - final Class rawClass, - final int paramIndex) { - final Class found = determineGenericClassParameterImpl(clazz, rawClass, paramIndex); - if (found == null) { - throw new AssertionError(clazz.getSimpleName() + " expected to extend " + rawClass.getSimpleName() + "<...>"); - } - return found; - } - - @SuppressWarnings("unchecked") - private static Class determineGenericClassParameterImpl( - final Class clazz, - final Class rawClass, - final int paramIndex) { - final Type genericClass = clazz.getGenericSuperclass(); - if (genericClass instanceof ParameterizedType) { - final ParameterizedType parameterizedType = (ParameterizedType) genericClass; - if (parameterizedType.getRawType() == rawClass) { - return (Class) parameterizedType.getActualTypeArguments()[paramIndex]; - } - } - if (clazz.getSuperclass() != Object.class) { - return determineGenericClassParameter(clazz.getSuperclass(), rawClass, paramIndex); - } - return null; - } - - @SuppressWarnings("unchecked") - public static > Enum asEnumValue(final Class type, final Object value) { - return Enum.valueOf((Class) type, value.toString()); - } - - public static Role newInstance(final Class clazz) { - return unchecked(() -> accessible(clazz.getDeclaredConstructor()).newInstance()); - } - - @FunctionalInterface - public interface ThrowingSupplier { - - T get() throws Exception; - } - - /** - * Makes the given object accessible as if it were public. - * - * @param accessible field or method - * @param type of accessible - * @return the given object - */ - private static T accessible(final T accessible) { - try { - accessible.setAccessible(true); - return accessible; - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Catches checked exceptions and wraps these into an unchecked RuntimeException. - *

- * Rationale: Checked exceptions are a controversial Java feature to begin with. - * They often mix error handling code into the normal flow of domain rules - * or other technical aspects which is not only hard to read but also violates - * the Single Responsibility Principle. Often this is even worse for expressions - * than it is for statements. - *

- * - * @param expression an expresion which returns a T and may throw a checked exception - * @param the result type of the expression - * @return the result of the expression - * @throws RuntimeException which wraps a checked exception thrown by the expression - */ - public static T unchecked(final ThrowingSupplier expression) { - try { - return expression.get(); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Calling a method on a potentially null object. Similar to the ?: operator in Kotlin. - * - * @param source some object of type T - * @param f some function mapping T to R - * @param the source type - * @param the result type - * @return the result of f if source is not null, null otherwise - */ - public static R of(T source, Function f) { - return Optional.ofNullable(source).map(f).orElse(null); - } - - /** - * Forces the initialization of the given class, this means, static initialization takes place. - * - * If the class is already initialized, this methods does nothing. - * - * @param clazz the class to be initialized - * @return the initialized class - * - */ - public static Class initialize(Class clazz) { - try { - Class.forName(clazz.getName(), true, clazz.getClassLoader()); - } catch (ClassNotFoundException e) { - throw new AssertionError(e); // Can't happen - } - return clazz; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/AccountResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/AccountResource.java deleted file mode 100644 index 4c0924d2..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/AccountResource.java +++ /dev/null @@ -1,183 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.security.SecurityUtils; -import org.hostsharing.hsadminng.service.MailService; -import org.hostsharing.hsadminng.service.UserService; -import org.hostsharing.hsadminng.service.dto.PasswordChangeDTO; -import org.hostsharing.hsadminng.service.dto.UserDTO; -import org.hostsharing.hsadminng.web.rest.errors.*; -import org.hostsharing.hsadminng.web.rest.vm.KeyAndPasswordVM; -import org.hostsharing.hsadminng.web.rest.vm.ManagedUserVM; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; - -import java.util.*; - -import javax.servlet.http.HttpServletRequest; -import javax.validation.Valid; - -/** - * REST controller for managing the current user's account. - */ -@RestController -@RequestMapping("/api") -public class AccountResource { - - private final Logger log = LoggerFactory.getLogger(AccountResource.class); - - private final UserRepository userRepository; - - private final UserService userService; - - private final MailService mailService; - - public AccountResource(UserRepository userRepository, UserService userService, MailService mailService) { - - this.userRepository = userRepository; - this.userService = userService; - this.mailService = mailService; - } - - /** - * POST /register : register the user. - * - * @param managedUserVM the managed user View Model - * @throws InvalidPasswordException 400 (Bad Request) if the password is incorrect - * @throws EmailAlreadyUsedException 400 (Bad Request) if the email is already used - * @throws LoginAlreadyUsedException 400 (Bad Request) if the login is already used - */ - @PostMapping("/register") - @ResponseStatus(HttpStatus.CREATED) - public void registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) { - if (!checkPasswordLength(managedUserVM.getPassword())) { - throw new InvalidPasswordException(); - } - User user = userService.registerUser(managedUserVM, managedUserVM.getPassword()); - mailService.sendActivationEmail(user); - } - - /** - * GET /activate : activate the registered user. - * - * @param key the activation key - * @throws RuntimeException 500 (Internal Server Error) if the user couldn't be activated - */ - @GetMapping("/activate") - public void activateAccount(@RequestParam(value = "key") String key) { - Optional user = userService.activateRegistration(key); - if (!user.isPresent()) { - throw new InternalServerErrorException("No user was found for this activation key"); - } - } - - /** - * GET /authenticate : check if the user is authenticated, and return its login. - * - * @param request the HTTP request - * @return the login if the user is authenticated - */ - @GetMapping("/authenticate") - public String isAuthenticated(HttpServletRequest request) { - log.debug("REST request to check if the current user is authenticated"); - return request.getRemoteUser(); - } - - /** - * GET /account : get the current user. - * - * @return the current user - * @throws RuntimeException 500 (Internal Server Error) if the user couldn't be returned - */ - @GetMapping("/account") - public UserDTO getAccount() { - return userService.getUserWithAuthorities() - .map(UserDTO::new) - .orElseThrow(() -> new InternalServerErrorException("User could not be found")); - } - - /** - * POST /account : update the current user information. - * - * @param userDTO the current user information - * @throws EmailAlreadyUsedException 400 (Bad Request) if the email is already used - * @throws RuntimeException 500 (Internal Server Error) if the user login wasn't found - */ - @PostMapping("/account") - public void saveAccount(@Valid @RequestBody UserDTO userDTO) { - String userLogin = SecurityUtils.getCurrentUserLogin() - .orElseThrow(() -> new InternalServerErrorException("Current user login not found")); - Optional existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()); - if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userLogin))) { - throw new EmailAlreadyUsedException(); - } - Optional user = userRepository.findOneByLogin(userLogin); - if (!user.isPresent()) { - throw new InternalServerErrorException("User could not be found"); - } - userService.updateUser( - userDTO.getFirstName(), - userDTO.getLastName(), - userDTO.getEmail(), - userDTO.getLangKey(), - userDTO.getImageUrl()); - } - - /** - * POST /account/change-password : changes the current user's password - * - * @param passwordChangeDto current and new password - * @throws InvalidPasswordException 400 (Bad Request) if the new password is incorrect - */ - @PostMapping(path = "/account/change-password") - public void changePassword(@RequestBody PasswordChangeDTO passwordChangeDto) { - if (!checkPasswordLength(passwordChangeDto.getNewPassword())) { - throw new InvalidPasswordException(); - } - userService.changePassword(passwordChangeDto.getCurrentPassword(), passwordChangeDto.getNewPassword()); - } - - /** - * POST /account/reset-password/init : Send an email to reset the password of the user - * - * @param mail the mail of the user - * @throws EmailNotFoundException 400 (Bad Request) if the email address is not registered - */ - @PostMapping(path = "/account/reset-password/init") - public void requestPasswordReset(@RequestBody String mail) { - mailService.sendPasswordResetMail( - userService.requestPasswordReset(mail) - .orElseThrow(EmailNotFoundException::new)); - } - - /** - * POST /account/reset-password/finish : Finish to reset the password of the user - * - * @param keyAndPassword the generated key and the new password - * @throws InvalidPasswordException 400 (Bad Request) if the password is incorrect - * @throws RuntimeException 500 (Internal Server Error) if the password could not be reset - */ - @PostMapping(path = "/account/reset-password/finish") - public void finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) { - if (!checkPasswordLength(keyAndPassword.getNewPassword())) { - throw new InvalidPasswordException(); - } - Optional user = userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey()); - - if (!user.isPresent()) { - throw new InternalServerErrorException("No user was found for this reset key"); - } - } - - private static boolean checkPasswordLength(String password) { - return !StringUtils.isEmpty(password) && - password.length() >= ManagedUserVM.PASSWORD_MIN_LENGTH && - password.length() <= ManagedUserVM.PASSWORD_MAX_LENGTH; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/AssetResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/AssetResource.java deleted file mode 100644 index ef5ebb17..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/AssetResource.java +++ /dev/null @@ -1,137 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.service.AssetQueryService; -import org.hostsharing.hsadminng.service.AssetService; -import org.hostsharing.hsadminng.service.dto.AssetCriteria; -import org.hostsharing.hsadminng.service.dto.AssetDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; -import org.hostsharing.hsadminng.web.rest.util.HeaderUtil; -import org.hostsharing.hsadminng.web.rest.util.PaginationUtil; - -import io.github.jhipster.web.util.ResponseUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Optional; - -import javax.validation.Valid; - -/** - * REST controller for managing Asset. - */ -@RestController -@RequestMapping("/api") -public class AssetResource { - - private final Logger log = LoggerFactory.getLogger(AssetResource.class); - - private static final String ENTITY_NAME = "asset"; - - private final AssetService assetService; - - private final AssetQueryService assetQueryService; - - public AssetResource(AssetService assetService, AssetQueryService assetQueryService) { - this.assetService = assetService; - this.assetQueryService = assetQueryService; - } - - /** - * POST /assets : Create a new asset. - * - * @param assetDTO the assetDTO to create - * @return the ResponseEntity with status 201 (Created) and with body the new assetDTO, or with status 400 (Bad Request) if - * the asset has already an ID - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PostMapping("/assets") - public ResponseEntity createAsset(@Valid @RequestBody AssetDTO assetDTO) throws URISyntaxException { - log.debug("REST request to save Asset : {}", assetDTO); - if (assetDTO.getId() != null) { - throw new BadRequestAlertException("A new asset cannot already have an ID", ENTITY_NAME, "idexists"); - } - AssetDTO result = assetService.save(assetDTO); - return ResponseEntity.created(new URI("/api/assets/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) - .body(result); - } - - /** - * PUT /assets : Updates an existing asset. - * - * @param assetDTO the assetDTO to update - * @return the ResponseEntity with status 200 (OK) and with body the updated assetDTO, - * or with status 400 (Bad Request) if the assetDTO is not valid, - * or with status 500 (Internal Server Error) if the assetDTO couldn't be updated - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PutMapping("/assets") - public ResponseEntity updateAsset(@Valid @RequestBody AssetDTO assetDTO) throws URISyntaxException { - log.debug("REST request to update Asset : {}", assetDTO); - // TODO mhoennig: Rather completely remove the endpoint? - throw new BadRequestAlertException("Assets are immutable", ENTITY_NAME, "assetTransactionImmutable"); - } - - /** - * GET /assets : get all the assets. - * - * @param pageable the pagination information - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the list of assets in body - */ - @GetMapping("/assets") - public ResponseEntity> getAllAssets(AssetCriteria criteria, Pageable pageable) { - log.debug("REST request to get Assets by criteria: {}", criteria); - Page page = assetQueryService.findByCriteria(criteria, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/assets"); - return ResponseEntity.ok().headers(headers).body(page.getContent()); - } - - /** - * GET /assets/count : count all the assets. - * - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the count in body - */ - @GetMapping("/assets/count") - public ResponseEntity countAssets(AssetCriteria criteria) { - log.debug("REST request to count Assets by criteria: {}", criteria); - return ResponseEntity.ok().body(assetQueryService.countByCriteria(criteria)); - } - - /** - * GET /assets/:id : get the "id" asset. - * - * @param id the id of the assetDTO to retrieve - * @return the ResponseEntity with status 200 (OK) and with body the assetDTO, or with status 404 (Not Found) - */ - @GetMapping("/assets/{id}") - public ResponseEntity getAsset(@PathVariable Long id) { - log.debug("REST request to get Asset : {}", id); - Optional assetDTO = assetService.findOne(id); - return ResponseUtil.wrapOrNotFound(assetDTO); - } - - /** - * DELETE /assets/:id : delete the "id" asset. - * - * @param id the id of the assetDTO to delete - * @return the ResponseEntity with status 200 (OK) - */ - @DeleteMapping("/assets/{id}") - public ResponseEntity deleteAsset(@PathVariable Long id) { - log.debug("REST request to delete Asset : {}", id); - // TODO mhoennig: Rather completely remove the endpoint? - throw new BadRequestAlertException("Asset are immutable", ENTITY_NAME, "assetTransactionImmutable"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/AuditResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/AuditResource.java deleted file mode 100644 index e723bf88..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/AuditResource.java +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.service.AuditEventService; -import org.hostsharing.hsadminng.web.rest.util.PaginationUtil; - -import io.github.jhipster.web.util.ResponseUtil; - -import org.springframework.boot.actuate.audit.AuditEvent; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.List; - -/** - * REST controller for getting the audit events. - */ -@RestController -@RequestMapping("/management/audits") -public class AuditResource { - - private final AuditEventService auditEventService; - - public AuditResource(AuditEventService auditEventService) { - this.auditEventService = auditEventService; - } - - /** - * GET /audits : get a page of AuditEvents. - * - * @param pageable the pagination information - * @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body - */ - @GetMapping - public ResponseEntity> getAll(Pageable pageable) { - Page page = auditEventService.findAll(pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits"); - return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); - } - - /** - * GET /audits : get a page of AuditEvents between the fromDate and toDate. - * - * @param fromDate the start of the time period of AuditEvents to get - * @param toDate the end of the time period of AuditEvents to get - * @param pageable the pagination information - * @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body - */ - @GetMapping(params = { "fromDate", "toDate" }) - public ResponseEntity> getByDates( - @RequestParam(value = "fromDate") LocalDate fromDate, - @RequestParam(value = "toDate") LocalDate toDate, - Pageable pageable) { - - Page page = auditEventService.findByDates( - fromDate.atStartOfDay(ZoneId.systemDefault()).toInstant(), - toDate.atStartOfDay(ZoneId.systemDefault()).plusDays(1).toInstant(), - pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits"); - return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); - } - - /** - * GET /audits/:id : get an AuditEvent by id. - * - * @param id the id of the entity to get - * @return the ResponseEntity with status 200 (OK) and the AuditEvent in body, or status 404 (Not Found) - */ - @GetMapping("/{id:.+}") - public ResponseEntity get(@PathVariable Long id) { - return ResponseUtil.wrapOrNotFound(auditEventService.find(id)); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/CustomerResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/CustomerResource.java deleted file mode 100644 index fe7ee8d7..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/CustomerResource.java +++ /dev/null @@ -1,142 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.service.CustomerQueryService; -import org.hostsharing.hsadminng.service.CustomerService; -import org.hostsharing.hsadminng.service.dto.CustomerCriteria; -import org.hostsharing.hsadminng.service.dto.CustomerDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; -import org.hostsharing.hsadminng.web.rest.util.HeaderUtil; -import org.hostsharing.hsadminng.web.rest.util.PaginationUtil; - -import io.github.jhipster.web.util.ResponseUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Optional; - -import javax.validation.Valid; - -/** - * REST controller for managing Customer. - */ -@RestController -@RequestMapping("/api") -public class CustomerResource { - - private final Logger log = LoggerFactory.getLogger(CustomerResource.class); - - private static final String ENTITY_NAME = "customer"; - - private final CustomerService customerService; - - private final CustomerQueryService customerQueryService; - - public CustomerResource(CustomerService customerService, CustomerQueryService customerQueryService) { - this.customerService = customerService; - this.customerQueryService = customerQueryService; - } - - /** - * POST /customers : Create a new customer. - * - * @param customerDTO the customerDTO to create - * @return the ResponseEntity with status 201 (Created) and with body the new customerDTO, or with status 400 (Bad Request) - * if the customer has already an ID - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PostMapping("/customers") - public ResponseEntity createCustomer(@Valid @RequestBody CustomerDTO customerDTO) throws URISyntaxException { - log.debug("REST request to save Customer : {}", customerDTO); - if (customerDTO.getId() != null) { - throw new BadRequestAlertException("A new customer cannot already have an ID", ENTITY_NAME, "idexists"); - } - CustomerDTO result = customerService.save(customerDTO); - return ResponseEntity.created(new URI("/api/customers/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) - .body(result); - } - - /** - * PUT /customers : Updates an existing customer. - * - * @param customerDTO the customerDTO to update - * @return the ResponseEntity with status 200 (OK) and with body the updated customerDTO, - * or with status 400 (Bad Request) if the customerDTO is not valid, - * or with status 500 (Internal Server Error) if the customerDTO couldn't be updated - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PutMapping("/customers") - public ResponseEntity updateCustomer(@RequestBody CustomerDTO customerDTO) { - log.debug("REST request to update Customer : {}", customerDTO); - if (customerDTO.getId() == null) { - throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); - } - CustomerDTO result = customerService.save(customerDTO); - return ResponseEntity.ok() - .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, customerDTO.getId().toString())) - .body(result); - } - - /** - * GET /customers : get all the customers. - * - * @param pageable the pagination information - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the list of customers in body - */ - @GetMapping("/customers") - public ResponseEntity> getAllCustomers(CustomerCriteria criteria, Pageable pageable) { - log.debug("REST request to get Customers by criteria: {}", criteria); - Page page = customerQueryService.findByCriteria(criteria, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/customers"); - return ResponseEntity.ok().headers(headers).body(page.getContent()); - } - - /** - * GET /customers/count : count all the customers. - * - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the count in body - */ - @GetMapping("/customers/count") - public ResponseEntity countCustomers(CustomerCriteria criteria) { - log.debug("REST request to count Customers by criteria: {}", criteria); - return ResponseEntity.ok().body(customerQueryService.countByCriteria(criteria)); - } - - /** - * GET /customers/:id : get the "id" customer. - * - * @param id the id of the customerDTO to retrieve - * @return the ResponseEntity with status 200 (OK) and with body the customerDTO, or with status 404 (Not Found) - */ - @GetMapping("/customers/{id}") - public ResponseEntity getCustomer(@PathVariable Long id) { - log.debug("REST request to get Customer : {}", id); - Optional customerDTO = customerService.findOne(id); - return ResponseUtil.wrapOrNotFound(customerDTO); - } - - /** - * DELETE /customers/:id : delete the "id" customer. - * - * @param id the id of the customerDTO to delete - * @return the ResponseEntity with status 200 (OK) - */ - @DeleteMapping("/customers/{id}") - public ResponseEntity deleteCustomer(@PathVariable Long id) { - log.debug("REST request to delete Customer : {}", id); - // TODO mhoennig: Rather completely remove the endpoint? - throw new BadRequestAlertException("Customres can't be deleted", ENTITY_NAME, "customerNotDeletable"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/LogsResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/LogsResource.java deleted file mode 100644 index 08315c87..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/LogsResource.java +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.web.rest.vm.LoggerVM; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.LoggerContext; - -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * Controller for view and managing Log Level at runtime. - */ -@RestController -@RequestMapping("/management") -public class LogsResource { - - @GetMapping("/logs") - public List getList() { - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - return context.getLoggerList() - .stream() - .map(LoggerVM::new) - .collect(Collectors.toList()); - } - - @PutMapping("/logs") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void changeLevel(@RequestBody LoggerVM jsonLogger) { - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - context.getLogger(jsonLogger.getName()).setLevel(Level.valueOf(jsonLogger.getLevel())); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/MembershipResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/MembershipResource.java deleted file mode 100644 index ff7d38c2..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/MembershipResource.java +++ /dev/null @@ -1,144 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.service.MembershipQueryService; -import org.hostsharing.hsadminng.service.MembershipService; -import org.hostsharing.hsadminng.service.dto.MembershipCriteria; -import org.hostsharing.hsadminng.service.dto.MembershipDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; -import org.hostsharing.hsadminng.web.rest.util.HeaderUtil; -import org.hostsharing.hsadminng.web.rest.util.PaginationUtil; - -import io.github.jhipster.web.util.ResponseUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Optional; - -import javax.validation.Valid; - -/** - * REST controller for managing Membership. - */ -@RestController -@RequestMapping("/api") -public class MembershipResource { - - private final Logger log = LoggerFactory.getLogger(MembershipResource.class); - - private static final String ENTITY_NAME = "membership"; - - private final MembershipService membershipService; - - private final MembershipQueryService membershipQueryService; - - public MembershipResource(MembershipService membershipService, MembershipQueryService membershipQueryService) { - this.membershipService = membershipService; - this.membershipQueryService = membershipQueryService; - } - - /** - * POST /memberships : Create a new membership. - * - * @param membershipDTO the membershipDTO to create - * @return the ResponseEntity with status 201 (Created) and with body the new membershipDTO, or with status 400 (Bad - * Request) if the membership has already an ID - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PostMapping("/memberships") - public ResponseEntity createMembership(@Valid @RequestBody MembershipDTO membershipDTO) - throws URISyntaxException { - log.debug("REST request to save Membership : {}", membershipDTO); - if (membershipDTO.getId() != null) { - throw new BadRequestAlertException("A new membership cannot already have an ID", ENTITY_NAME, "idexists"); - } - MembershipDTO result = membershipService.save(membershipDTO); - return ResponseEntity.created(new URI("/api/memberships/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) - .body(result); - } - - /** - * PUT /memberships : Updates an existing membership. - * - * @param membershipDTO the membershipDTO to update - * @return the ResponseEntity with status 200 (OK) and with body the updated membershipDTO, - * or with status 400 (Bad Request) if the membershipDTO is not valid, - * or with status 500 (Internal Server Error) if the membershipDTO couldn't be updated - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PutMapping("/memberships") - public ResponseEntity updateMembership(@Valid @RequestBody MembershipDTO membershipDTO) - throws URISyntaxException { - log.debug("REST request to update Membership : {}", membershipDTO); - if (membershipDTO.getId() == null) { - throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); - } - MembershipDTO result = membershipService.save(membershipDTO); - return ResponseEntity.ok() - .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, membershipDTO.getId().toString())) - .body(result); - } - - /** - * GET /memberships : get all the memberships. - * - * @param pageable the pagination information - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the list of memberships in body - */ - @GetMapping("/memberships") - public ResponseEntity> getAllMemberships(MembershipCriteria criteria, Pageable pageable) { - log.debug("REST request to get Memberships by criteria: {}", criteria); - Page page = membershipQueryService.findByCriteria(criteria, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/memberships"); - return ResponseEntity.ok().headers(headers).body(page.getContent()); - } - - /** - * GET /memberships/count : count all the memberships. - * - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the count in body - */ - @GetMapping("/memberships/count") - public ResponseEntity countMemberships(MembershipCriteria criteria) { - log.debug("REST request to count Memberships by criteria: {}", criteria); - return ResponseEntity.ok().body(membershipQueryService.countByCriteria(criteria)); - } - - /** - * GET /memberships/:id : get the "id" membership. - * - * @param id the id of the membershipDTO to retrieve - * @return the ResponseEntity with status 200 (OK) and with body the membershipDTO, or with status 404 (Not Found) - */ - @GetMapping("/memberships/{id}") - public ResponseEntity getMembership(@PathVariable Long id) { - log.debug("REST request to get Membership : {}", id); - Optional membershipDTO = membershipService.findOne(id); - return ResponseUtil.wrapOrNotFound(membershipDTO); - } - - /** - * DELETE /memberships/:id : delete the "id" membership. - * - * @param id the id of the membershipDTO to delete - * @return the ResponseEntity with status 200 (OK) - */ - @DeleteMapping("/memberships/{id}") - public ResponseEntity deleteMembership(@PathVariable Long id) { - log.debug("REST request to delete Membership : {}", id); - // TODO mhoennig: Rather completely remove the endpoint? - throw new BadRequestAlertException("Memberships can't be deleted", ENTITY_NAME, "membershipNotDeletable"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/SepaMandateResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/SepaMandateResource.java deleted file mode 100644 index 58a1dd27..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/SepaMandateResource.java +++ /dev/null @@ -1,144 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.service.SepaMandateQueryService; -import org.hostsharing.hsadminng.service.SepaMandateService; -import org.hostsharing.hsadminng.service.dto.SepaMandateCriteria; -import org.hostsharing.hsadminng.service.dto.SepaMandateDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; -import org.hostsharing.hsadminng.web.rest.util.HeaderUtil; -import org.hostsharing.hsadminng.web.rest.util.PaginationUtil; - -import io.github.jhipster.web.util.ResponseUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Optional; - -import javax.validation.Valid; - -/** - * REST controller for managing SepaMandate. - */ -@RestController -@RequestMapping("/api") -public class SepaMandateResource { - - private final Logger log = LoggerFactory.getLogger(SepaMandateResource.class); - - private static final String ENTITY_NAME = "sepaMandate"; - - private final SepaMandateService sepaMandateService; - - private final SepaMandateQueryService sepaMandateQueryService; - - public SepaMandateResource(SepaMandateService sepaMandateService, SepaMandateQueryService sepaMandateQueryService) { - this.sepaMandateService = sepaMandateService; - this.sepaMandateQueryService = sepaMandateQueryService; - } - - /** - * POST /sepa-mandates : Create a new sepaMandate. - * - * @param sepaMandateDTO the sepaMandateDTO to create - * @return the ResponseEntity with status 201 (Created) and with body the new sepaMandateDTO, or with status 400 (Bad - * Request) if the sepaMandate has already an ID - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PostMapping("/sepa-mandates") - public ResponseEntity createSepaMandate(@Valid @RequestBody SepaMandateDTO sepaMandateDTO) - throws URISyntaxException { - log.debug("REST request to save SepaMandate : {}", sepaMandateDTO); - if (sepaMandateDTO.getId() != null) { - throw new BadRequestAlertException("A new sepaMandate cannot already have an ID", ENTITY_NAME, "idexists"); - } - SepaMandateDTO result = sepaMandateService.save(sepaMandateDTO); - return ResponseEntity.created(new URI("/api/sepa-mandates/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) - .body(result); - } - - /** - * PUT /sepa-mandates : Updates an existing sepaMandate. - * - * @param sepaMandateDTO the sepaMandateDTO to update - * @return the ResponseEntity with status 200 (OK) and with body the updated sepaMandateDTO, - * or with status 400 (Bad Request) if the sepaMandateDTO is not valid, - * or with status 500 (Internal Server Error) if the sepaMandateDTO couldn't be updated - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PutMapping("/sepa-mandates") - public ResponseEntity updateSepaMandate(@Valid @RequestBody SepaMandateDTO sepaMandateDTO) - throws URISyntaxException { - log.debug("REST request to update SepaMandate : {}", sepaMandateDTO); - if (sepaMandateDTO.getId() == null) { - throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); - } - SepaMandateDTO result = sepaMandateService.save(sepaMandateDTO); - return ResponseEntity.ok() - .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, sepaMandateDTO.getId().toString())) - .body(result); - } - - /** - * GET /sepa-mandates : get all the sepaMandates. - * - * @param pageable the pagination information - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the list of sepaMandates in body - */ - @GetMapping("/sepa-mandates") - public ResponseEntity> getAllSepaMandates(SepaMandateCriteria criteria, Pageable pageable) { - log.debug("REST request to get SepaMandates by criteria: {}", criteria); - Page page = sepaMandateQueryService.findByCriteria(criteria, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/sepa-mandates"); - return ResponseEntity.ok().headers(headers).body(page.getContent()); - } - - /** - * GET /sepa-mandates/count : count all the sepaMandates. - * - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the count in body - */ - @GetMapping("/sepa-mandates/count") - public ResponseEntity countSepaMandates(SepaMandateCriteria criteria) { - log.debug("REST request to count SepaMandates by criteria: {}", criteria); - return ResponseEntity.ok().body(sepaMandateQueryService.countByCriteria(criteria)); - } - - /** - * GET /sepa-mandates/:id : get the "id" sepaMandate. - * - * @param id the id of the sepaMandateDTO to retrieve - * @return the ResponseEntity with status 200 (OK) and with body the sepaMandateDTO, or with status 404 (Not Found) - */ - @GetMapping("/sepa-mandates/{id}") - public ResponseEntity getSepaMandate(@PathVariable Long id) { - log.debug("REST request to get SepaMandate : {}", id); - Optional sepaMandateDTO = sepaMandateService.findOne(id); - return ResponseUtil.wrapOrNotFound(sepaMandateDTO); - } - - /** - * DELETE /sepa-mandates/:id : delete the "id" sepaMandate. - * - * @param id the id of the sepaMandateDTO to delete - * @return the ResponseEntity with status 200 (OK) - */ - @DeleteMapping("/sepa-mandates/{id}") - public ResponseEntity deleteSepaMandate(@PathVariable Long id) { - log.debug("REST request to delete SepaMandate : {}", id); - sepaMandateService.delete(id); - return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build(); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/ShareResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/ShareResource.java deleted file mode 100644 index f34d0ccb..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/ShareResource.java +++ /dev/null @@ -1,137 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.service.ShareQueryService; -import org.hostsharing.hsadminng.service.ShareService; -import org.hostsharing.hsadminng.service.dto.ShareCriteria; -import org.hostsharing.hsadminng.service.dto.ShareDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; -import org.hostsharing.hsadminng.web.rest.util.HeaderUtil; -import org.hostsharing.hsadminng.web.rest.util.PaginationUtil; - -import io.github.jhipster.web.util.ResponseUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Optional; - -import javax.validation.Valid; - -/** - * REST controller for managing Share. - */ -@RestController -@RequestMapping("/api") -public class ShareResource { - - private final Logger log = LoggerFactory.getLogger(ShareResource.class); - - private static final String ENTITY_NAME = "share"; - - private final ShareService shareService; - - private final ShareQueryService shareQueryService; - - public ShareResource(ShareService shareService, ShareQueryService shareQueryService) { - this.shareService = shareService; - this.shareQueryService = shareQueryService; - } - - /** - * POST /shares : Create a new share. - * - * @param shareDTO the shareDTO to create - * @return the ResponseEntity with status 201 (Created) and with body the new shareDTO, or with status 400 (Bad Request) if - * the share has already an ID - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PostMapping("/shares") - public ResponseEntity createShare(@Valid @RequestBody ShareDTO shareDTO) throws URISyntaxException { - log.debug("REST request to save Share : {}", shareDTO); - if (shareDTO.getId() != null) { - throw new BadRequestAlertException("A new share cannot already have an ID", ENTITY_NAME, "idexists"); - } - ShareDTO result = shareService.save(shareDTO); - return ResponseEntity.created(new URI("/api/shares/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) - .body(result); - } - - /** - * PUT /shares : Updates an existing share. - * - * @param shareDTO the shareDTO to update - * @return the ResponseEntity with status 200 (OK) and with body the updated shareDTO, - * or with status 400 (Bad Request) if the shareDTO is not valid, - * or with status 500 (Internal Server Error) if the shareDTO couldn't be updated - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PutMapping("/shares") - public ResponseEntity updateShare(@Valid @RequestBody ShareDTO shareDTO) throws URISyntaxException { - log.debug("REST request to update Share : {}", shareDTO); - // TODO mhoennig: Rather completely remove the endpoint? - throw new BadRequestAlertException("Shares are immutable", ENTITY_NAME, "shareTransactionImmutable"); - } - - /** - * GET /shares : get all the shares. - * - * @param pageable the pagination information - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the list of shares in body - */ - @GetMapping("/shares") - public ResponseEntity> getAllShares(ShareCriteria criteria, Pageable pageable) { - log.debug("REST request to get Shares by criteria: {}", criteria); - Page page = shareQueryService.findByCriteria(criteria, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/shares"); - return ResponseEntity.ok().headers(headers).body(page.getContent()); - } - - /** - * GET /shares/count : count all the shares. - * - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the count in body - */ - @GetMapping("/shares/count") - public ResponseEntity countShares(ShareCriteria criteria) { - log.debug("REST request to count Shares by criteria: {}", criteria); - return ResponseEntity.ok().body(shareQueryService.countByCriteria(criteria)); - } - - /** - * GET /shares/:id : get the "id" share. - * - * @param id the id of the shareDTO to retrieve - * @return the ResponseEntity with status 200 (OK) and with body the shareDTO, or with status 404 (Not Found) - */ - @GetMapping("/shares/{id}") - public ResponseEntity getShare(@PathVariable Long id) { - log.debug("REST request to get Share : {}", id); - Optional shareDTO = shareService.findOne(id); - return ResponseUtil.wrapOrNotFound(shareDTO); - } - - /** - * DELETE /shares/:id : delete the "id" share. - * - * @param id the id of the shareDTO to delete - * @return the ResponseEntity with status 200 (OK) - */ - @DeleteMapping("/shares/{id}") - public ResponseEntity deleteShare(@PathVariable Long id) { - log.debug("REST request to delete Share : {}", id); - // TODO mhoennig: Rather completely remove the endpoint? - throw new BadRequestAlertException("Shares are immutable", ENTITY_NAME, "shareTransactionImmutable"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/UserJWTController.java b/src/main/java/org/hostsharing/hsadminng/web/rest/UserJWTController.java deleted file mode 100644 index db3ef0ff..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/UserJWTController.java +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.security.jwt.JWTFilter; -import org.hostsharing.hsadminng.security.jwt.TokenProvider; -import org.hostsharing.hsadminng.web.rest.vm.LoginVM; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; - -/** - * Controller to authenticate users. - */ -@RestController -@RequestMapping("/api") -public class UserJWTController { - - private final TokenProvider tokenProvider; - - private final AuthenticationManager authenticationManager; - - public UserJWTController(TokenProvider tokenProvider, AuthenticationManager authenticationManager) { - this.tokenProvider = tokenProvider; - this.authenticationManager = authenticationManager; - } - - @PostMapping("/authenticate") - public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM) { - - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( - loginVM.getUsername(), - loginVM.getPassword()); - - Authentication authentication = this.authenticationManager.authenticate(authenticationToken); - SecurityContextHolder.getContext().setAuthentication(authentication); - boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe(); - String jwt = tokenProvider.createToken(authentication, rememberMe); - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); - return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK); - } - - /** - * Object to return as body in JWT Authentication. - */ - static class JWTToken { - - private String idToken; - - JWTToken(String idToken) { - this.idToken = idToken; - } - - @JsonProperty("id_token") - String getIdToken() { - return idToken; - } - - void setIdToken(String idToken) { - this.idToken = idToken; - } - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/UserResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/UserResource.java deleted file mode 100644 index eaff107e..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/UserResource.java +++ /dev/null @@ -1,188 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.config.Constants; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.MailService; -import org.hostsharing.hsadminng.service.UserService; -import org.hostsharing.hsadminng.service.dto.UserDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; -import org.hostsharing.hsadminng.web.rest.errors.EmailAlreadyUsedException; -import org.hostsharing.hsadminng.web.rest.errors.LoginAlreadyUsedException; -import org.hostsharing.hsadminng.web.rest.util.HeaderUtil; -import org.hostsharing.hsadminng.web.rest.util.PaginationUtil; - -import io.github.jhipster.web.util.ResponseUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.*; - -import javax.validation.Valid; - -/** - * REST controller for managing users. - *

- * This class accesses the User entity, and needs to fetch its collection of authorities. - *

- * For a normal use-case, it would be better to have an eager relationship between User and Authority, - * and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join - * which would be good for performance. - *

- * We use a View Model and a DTO for 3 reasons: - *

    - *
  • We want to keep a lazy association between the user and the authorities, because people will - * quite often do relationships with the user, and we don't want them to get the authorities all - * the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users' - * application because of this use-case.
  • - *
  • Not having an outer join causes n+1 requests to the database. This is not a real issue as - * we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests, - * but then all authorities come from the cache, so in fact it's much better than doing an outer join - * (which will get lots of data from the database, for each HTTP call).
  • - *
  • As this manages users, for security reasons, we'd rather have a DTO layer.
  • - *
- *

- * Another option would be to have a specific JPA entity graph to handle this case. - */ -@RestController -@RequestMapping("/api") -public class UserResource { - - private final Logger log = LoggerFactory.getLogger(UserResource.class); - - private final UserService userService; - - private final UserRepository userRepository; - - private final MailService mailService; - - public UserResource(UserService userService, UserRepository userRepository, MailService mailService) { - - this.userService = userService; - this.userRepository = userRepository; - this.mailService = mailService; - } - - /** - * POST /users : Creates a new user. - *

- * Creates a new user if the login and email are not already used, and sends an - * mail with an activation link. - * The user needs to be activated on creation. - * - * @param userDTO the user to create - * @return the ResponseEntity with status 201 (Created) and with body the new user, or with status 400 (Bad Request) if the - * login or email is already in use - * @throws URISyntaxException if the Location URI syntax is incorrect - * @throws BadRequestAlertException 400 (Bad Request) if the login or email is already in use - */ - @PostMapping("/users") - @PreAuthorize("hasRole(\"" + AuthoritiesConstants.ADMIN + "\")") - public ResponseEntity createUser(@Valid @RequestBody UserDTO userDTO) throws URISyntaxException { - log.debug("REST request to save User : {}", userDTO); - - if (userDTO.getId() != null) { - throw new BadRequestAlertException("A new user cannot already have an ID", "userManagement", "idexists"); - // Lowercase the user login before comparing with database - } else if (userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).isPresent()) { - throw new LoginAlreadyUsedException(); - } else if (userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()).isPresent()) { - throw new EmailAlreadyUsedException(); - } else { - User newUser = userService.createUser(userDTO); - mailService.sendCreationEmail(newUser); - return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin())) - .headers(HeaderUtil.createAlert("userManagement.created", newUser.getLogin())) - .body(newUser); - } - } - - /** - * PUT /users : Updates an existing User. - * - * @param userDTO the user to update - * @return the ResponseEntity with status 200 (OK) and with body the updated user - * @throws EmailAlreadyUsedException 400 (Bad Request) if the email is already in use - * @throws LoginAlreadyUsedException 400 (Bad Request) if the login is already in use - */ - @PutMapping("/users") - @PreAuthorize("hasRole(\"" + AuthoritiesConstants.ADMIN + "\")") - public ResponseEntity updateUser(@Valid @RequestBody UserDTO userDTO) { - log.debug("REST request to update User : {}", userDTO); - Optional existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()); - if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) { - throw new EmailAlreadyUsedException(); - } - existingUser = userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()); - if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) { - throw new LoginAlreadyUsedException(); - } - Optional updatedUser = userService.updateUser(userDTO); - - return ResponseUtil.wrapOrNotFound( - updatedUser, - HeaderUtil.createAlert("userManagement.updated", userDTO.getLogin())); - } - - /** - * GET /users : get all users. - * - * @param pageable the pagination information - * @return the ResponseEntity with status 200 (OK) and with body all users - */ - @GetMapping("/users") - public ResponseEntity> getAllUsers(Pageable pageable) { - final Page page = userService.getAllManagedUsers(pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users"); - return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); - } - - /** - * @return a string list of the all of the roles - */ - @GetMapping("/users/authorities") - @PreAuthorize("hasRole(\"" + AuthoritiesConstants.ADMIN + "\")") - public List getAuthorities() { - return userService.getAuthorities(); - } - - /** - * GET /users/:login : get the "login" user. - * - * @param login the login of the user to find - * @return the ResponseEntity with status 200 (OK) and with body the "login" user, or with status 404 (Not Found) - */ - @GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}") - public ResponseEntity getUser(@PathVariable String login) { - log.debug("REST request to get User : {}", login); - return ResponseUtil.wrapOrNotFound( - userService.getUserWithAuthoritiesByLogin(login) - .map(UserDTO::new)); - } - - /** - * DELETE /users/:login : delete the "login" User. - * - * @param login the login of the user to delete - * @return the ResponseEntity with status 200 (OK) - */ - @DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}") - @PreAuthorize("hasRole(\"" + AuthoritiesConstants.ADMIN + "\")") - public ResponseEntity deleteUser(@PathVariable String login) { - log.debug("REST request to delete User: {}", login); - userService.deleteUser(login); - return ResponseEntity.ok().headers(HeaderUtil.createAlert("userManagement.deleted", login)).build(); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResource.java b/src/main/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResource.java deleted file mode 100644 index 8417f65c..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResource.java +++ /dev/null @@ -1,148 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import org.hostsharing.hsadminng.domain.UserRoleAssignment; -import org.hostsharing.hsadminng.service.UserRoleAssignmentQueryService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.dto.UserRoleAssignmentCriteria; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; -import org.hostsharing.hsadminng.web.rest.util.HeaderUtil; -import org.hostsharing.hsadminng.web.rest.util.PaginationUtil; - -import io.github.jhipster.web.util.ResponseUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Optional; - -import javax.validation.Valid; - -/** - * REST controller for managing UserRoleAssignment. - */ -@RestController -@RequestMapping("/api") -public class UserRoleAssignmentResource { - - private final Logger log = LoggerFactory.getLogger(UserRoleAssignmentResource.class); - - private static final String ENTITY_NAME = "userRoleAssignment"; - - private final UserRoleAssignmentService userRoleAssignmentService; - - private final UserRoleAssignmentQueryService userRoleAssignmentQueryService; - - public UserRoleAssignmentResource( - UserRoleAssignmentService userRoleAssignmentService, - UserRoleAssignmentQueryService userRoleAssignmentQueryService) { - this.userRoleAssignmentService = userRoleAssignmentService; - this.userRoleAssignmentQueryService = userRoleAssignmentQueryService; - } - - /** - * POST /user-role-assignments : Create a new userRoleAssignment. - * - * @param userRoleAssignment the userRoleAssignment to create - * @return the ResponseEntity with status 201 (Created) and with body the new userRoleAssignment, or with status 400 (Bad - * Request) if the userRoleAssignment has already an ID - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PostMapping("/user-role-assignments") - public ResponseEntity createUserRoleAssignment( - @Valid @RequestBody UserRoleAssignment userRoleAssignment) throws URISyntaxException { - log.debug("REST request to save UserRoleAssignment : {}", userRoleAssignment); - if (userRoleAssignment.getId() != null) { - throw new BadRequestAlertException("A new userRoleAssignment cannot already have an ID", ENTITY_NAME, "idexists"); - } - UserRoleAssignment result = userRoleAssignmentService.save(userRoleAssignment); - return ResponseEntity.created(new URI("/api/user-role-assignments/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) - .body(result); - } - - /** - * PUT /user-role-assignments : Updates an existing userRoleAssignment. - * - * @param userRoleAssignment the userRoleAssignment to update - * @return the ResponseEntity with status 200 (OK) and with body the updated userRoleAssignment, - * or with status 400 (Bad Request) if the userRoleAssignment is not valid, - * or with status 500 (Internal Server Error) if the userRoleAssignment couldn't be updated - * @throws URISyntaxException if the Location URI syntax is incorrect - */ - @PutMapping("/user-role-assignments") - public ResponseEntity updateUserRoleAssignment( - @Valid @RequestBody UserRoleAssignment userRoleAssignment) throws URISyntaxException { - log.debug("REST request to update UserRoleAssignment : {}", userRoleAssignment); - if (userRoleAssignment.getId() == null) { - throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); - } - UserRoleAssignment result = userRoleAssignmentService.save(userRoleAssignment); - return ResponseEntity.ok() - .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, userRoleAssignment.getId().toString())) - .body(result); - } - - /** - * GET /user-role-assignments : get all the userRoleAssignments. - * - * @param pageable the pagination information - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the list of userRoleAssignments in body - */ - @GetMapping("/user-role-assignments") - public ResponseEntity> getAllUserRoleAssignments( - UserRoleAssignmentCriteria criteria, - Pageable pageable) { - log.debug("REST request to get UserRoleAssignments by criteria: {}", criteria); - Page page = userRoleAssignmentQueryService.findByCriteria(criteria, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/user-role-assignments"); - return ResponseEntity.ok().headers(headers).body(page.getContent()); - } - - /** - * GET /user-role-assignments/count : count all the userRoleAssignments. - * - * @param criteria the criterias which the requested entities should match - * @return the ResponseEntity with status 200 (OK) and the count in body - */ - @GetMapping("/user-role-assignments/count") - public ResponseEntity countUserRoleAssignments(UserRoleAssignmentCriteria criteria) { - log.debug("REST request to count UserRoleAssignments by criteria: {}", criteria); - return ResponseEntity.ok().body(userRoleAssignmentQueryService.countByCriteria(criteria)); - } - - /** - * GET /user-role-assignments/:id : get the "id" userRoleAssignment. - * - * @param id the id of the userRoleAssignment to retrieve - * @return the ResponseEntity with status 200 (OK) and with body the userRoleAssignment, or with status 404 (Not Found) - */ - @GetMapping("/user-role-assignments/{id}") - public ResponseEntity getUserRoleAssignment(@PathVariable Long id) { - log.debug("REST request to get UserRoleAssignment : {}", id); - Optional userRoleAssignment = userRoleAssignmentService.findOne(id); - return ResponseUtil.wrapOrNotFound(userRoleAssignment); - } - - /** - * DELETE /user-role-assignments/:id : delete the "id" userRoleAssignment. - * - * @param id the id of the userRoleAssignment to delete - * @return the ResponseEntity with status 200 (OK) - */ - @DeleteMapping("/user-role-assignments/{id}") - public ResponseEntity deleteUserRoleAssignment(@PathVariable Long id) { - log.debug("REST request to delete UserRoleAssignment : {}", id); - userRoleAssignmentService.delete(id); - return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build(); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/BadRequestAlertException.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/BadRequestAlertException.java deleted file mode 100644 index eb87b2ef..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/BadRequestAlertException.java +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import org.zalando.problem.AbstractThrowableProblem; -import org.zalando.problem.Status; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -public class BadRequestAlertException extends AbstractThrowableProblem { - - private static final long serialVersionUID = 1L; - - private final String param; - - private final String errorKey; - - public BadRequestAlertException(String defaultMessage, String param, String errorKey) { - this(ErrorConstants.DEFAULT_TYPE, defaultMessage, param, errorKey); - } - - public BadRequestAlertException(URI type, String defaultMessage, String param, String errorKey) { - super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(param, errorKey)); - this.param = param; - this.errorKey = errorKey; - } - - public String getParam() { - return param; - } - - public String getErrorKey() { - return errorKey; - } - - private static Map getAlertParameters(String param, String errorKey) { - Map parameters = new HashMap<>(); - parameters.put("message", "error." + errorKey); - parameters.put("params", param); - return parameters; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/CustomParameterizedException.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/CustomParameterizedException.java deleted file mode 100644 index 359e1d8b..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/CustomParameterizedException.java +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import static org.zalando.problem.Status.BAD_REQUEST; - -import org.zalando.problem.AbstractThrowableProblem; - -import java.util.HashMap; -import java.util.Map; - -/** - * Custom, parameterized exception, which can be translated on the client side. - * For example: - * - *

- * throw new CustomParameterizedException("myCustomError", "hello", "world");
- * 
- * - * Can be translated with: - * - *
- * "error.myCustomError" :  "The server says {{param0}} to {{param1}}"
- * 
- */ -public class CustomParameterizedException extends AbstractThrowableProblem { - - private static final long serialVersionUID = 1L; - - private static final String PARAM = "param"; - - public CustomParameterizedException(String message, String... params) { - this(message, toParamMap(params)); - } - - public CustomParameterizedException(String message, Map paramMap) { - super(ErrorConstants.PARAMETERIZED_TYPE, - "Parameterized Exception", - BAD_REQUEST, - null, - null, - null, - toProblemParameters(message, paramMap)); - } - - public static Map toParamMap(String... params) { - Map paramMap = new HashMap<>(); - if (params != null && params.length > 0) { - for (int i = 0; i < params.length; i++) { - paramMap.put(PARAM + i, params[i]); - } - } - return paramMap; - } - - public static Map toProblemParameters(String message, Map paramMap) { - Map parameters = new HashMap<>(); - parameters.put("message", message); - parameters.put("params", paramMap); - return parameters; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/EmailAlreadyUsedException.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/EmailAlreadyUsedException.java deleted file mode 100644 index 473297c1..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/EmailAlreadyUsedException.java +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -public class EmailAlreadyUsedException extends BadRequestAlertException { - - private static final long serialVersionUID = 1L; - - public EmailAlreadyUsedException() { - super(ErrorConstants.EMAIL_ALREADY_USED_TYPE, "Email is already in use!", "userManagement", "emailexists"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/EmailNotFoundException.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/EmailNotFoundException.java deleted file mode 100644 index 31ffcade..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/EmailNotFoundException.java +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import org.zalando.problem.AbstractThrowableProblem; -import org.zalando.problem.Status; - -public class EmailNotFoundException extends AbstractThrowableProblem { - - private static final long serialVersionUID = 1L; - - public EmailNotFoundException() { - super(ErrorConstants.EMAIL_NOT_FOUND_TYPE, "Email address not registered", Status.BAD_REQUEST); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/ErrorConstants.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/ErrorConstants.java deleted file mode 100644 index fff79e48..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/ErrorConstants.java +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import java.net.URI; - -public final class ErrorConstants { - - public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure"; - public static final String ERR_VALIDATION = "error.validation"; - public static final String ERR_VALIDATION_DUPLICATE = "entity.validation.duplicate"; - - public static final String PROBLEM_BASE_URL = "https://www.jhipster.tech/problem"; - - public static final URI DEFAULT_TYPE = URI.create(PROBLEM_BASE_URL + "/problem-with-message"); - public static final URI CONSTRAINT_VIOLATION_TYPE = URI.create(PROBLEM_BASE_URL + "/constraint-violation"); - public static final URI PARAMETERIZED_TYPE = URI.create(PROBLEM_BASE_URL + "/parameterized"); - public static final URI ENTITY_NOT_FOUND_TYPE = URI.create(PROBLEM_BASE_URL + "/entity-not-found"); - public static final URI INVALID_PASSWORD_TYPE = URI.create(PROBLEM_BASE_URL + "/invalid-password"); - public static final URI EMAIL_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/email-already-used"); - public static final URI LOGIN_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/login-already-used"); - public static final URI EMAIL_NOT_FOUND_TYPE = URI.create(PROBLEM_BASE_URL + "/email-not-found"); - - private ErrorConstants() { - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslator.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslator.java deleted file mode 100644 index 5aa2a5b9..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslator.java +++ /dev/null @@ -1,137 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import static org.hostsharing.hsadminng.web.rest.errors.ErrorConstants.*; - -import org.hostsharing.hsadminng.web.rest.util.HeaderUtil; - -import org.springframework.dao.ConcurrencyFailureException; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.context.request.NativeWebRequest; -import org.zalando.problem.DefaultProblem; -import org.zalando.problem.Problem; -import org.zalando.problem.ProblemBuilder; -import org.zalando.problem.Status; -import org.zalando.problem.spring.web.advice.ProblemHandling; -import org.zalando.problem.violations.ConstraintViolationProblem; - -import java.util.List; -import java.util.NoSuchElementException; -import java.util.stream.Collectors; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; - -/** - * Controller advice to translate the server side exceptions to client-friendly json structures. - * The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807) - */ -@ControllerAdvice -public class ExceptionTranslator implements ProblemHandling { - - private static final String FIELD_ERRORS_KEY = "fieldErrors"; - private static final String MESSAGE_KEY = "message"; - private static final String PATH_KEY = "path"; - private static final String VIOLATIONS_KEY = "violations"; - - /** - * Post-process the Problem payload to add the message key for the front-end if needed - */ - @Override - public ResponseEntity process(@Nullable ResponseEntity entity, NativeWebRequest request) { - if (entity == null) { - return entity; - } - Problem problem = entity.getBody(); - if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) { - return entity; - } - ProblemBuilder builder = Problem.builder() - .withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? DEFAULT_TYPE : problem.getType()) - .withStatus(problem.getStatus()) - .withTitle(problem.getTitle()) - .with(PATH_KEY, request.getNativeRequest(HttpServletRequest.class).getRequestURI()); - - if (problem instanceof ConstraintViolationProblem) { - builder - .with(VIOLATIONS_KEY, ((ConstraintViolationProblem) problem).getViolations()) - .with(MESSAGE_KEY, ERR_VALIDATION); - } else { - builder - .withCause(((DefaultProblem) problem).getCause()) - .withDetail(problem.getDetail()) - .withInstance(problem.getInstance()); - problem.getParameters().forEach(builder::with); - if (!problem.getParameters().containsKey(MESSAGE_KEY) && problem.getStatus() != null) { - builder.with(MESSAGE_KEY, "error.http." + problem.getStatus().getStatusCode()); - } - } - return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode()); - } - - @Override - public ResponseEntity handleMethodArgumentNotValid( - MethodArgumentNotValidException ex, - @Nonnull NativeWebRequest request) { - BindingResult result = ex.getBindingResult(); - List fieldErrors = result.getFieldErrors() - .stream() - .map(f -> new FieldErrorVM(f.getObjectName(), f.getField(), f.getCode())) - .collect(Collectors.toList()); - - Problem problem = Problem.builder() - .withType(CONSTRAINT_VIOLATION_TYPE) - .withTitle("Method argument not valid") - .withStatus(defaultConstraintViolationStatus()) - .with(MESSAGE_KEY, ERR_VALIDATION) - .with(FIELD_ERRORS_KEY, fieldErrors) - .build(); - return create(ex, problem, request); - } - - @ExceptionHandler - public ResponseEntity handleNoSuchElementException(NoSuchElementException ex, NativeWebRequest request) { - Problem problem = Problem.builder() - .withStatus(Status.NOT_FOUND) - .with(MESSAGE_KEY, ENTITY_NOT_FOUND_TYPE) - .build(); - return create(ex, problem, request); - } - - @ExceptionHandler - public ResponseEntity handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) { - return create(ex, request, HeaderUtil.createFailureAlert(ex.getParam(), ex.getErrorKey(), ex.getMessage())); - } - - @ExceptionHandler - public ResponseEntity handleConcurrencyFailure(ConcurrencyFailureException ex, NativeWebRequest request) { - Problem problem = Problem.builder() - .withStatus(Status.CONFLICT) - .with(MESSAGE_KEY, ERR_CONCURRENCY_FAILURE) - .build(); - return create(ex, problem, request); - } - - @ExceptionHandler(DataIntegrityViolationException.class) - @ResponseBody - @ResponseStatus(HttpStatus.CONFLICT) - public ResponseEntity processDataIntegrityViolationException( - DataIntegrityViolationException exception, - NativeWebRequest request) { - // UX_CUSTOMER_JHI_NUMBER_INDEX_5 - Problem problem = Problem.builder() - .withStatus(Status.CONFLICT) - .with(MESSAGE_KEY, ERR_VALIDATION_DUPLICATE) - .build(); - return create(exception, problem, request); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/FieldErrorVM.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/FieldErrorVM.java deleted file mode 100644 index 353102ec..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/FieldErrorVM.java +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import java.io.Serializable; - -public class FieldErrorVM implements Serializable { - - private static final long serialVersionUID = 1L; - - private final String objectName; - - private final String field; - - private final String message; - - public FieldErrorVM(String dto, String field, String message) { - this.objectName = dto; - this.field = field; - this.message = message; - } - - public String getObjectName() { - return objectName; - } - - public String getField() { - return field; - } - - public String getMessage() { - return message; - } - -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/InternalServerErrorException.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/InternalServerErrorException.java deleted file mode 100644 index 3221fcca..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/InternalServerErrorException.java +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import org.zalando.problem.AbstractThrowableProblem; -import org.zalando.problem.Status; - -/** - * Simple exception with a message, that returns an Internal Server Error code. - */ -public class InternalServerErrorException extends AbstractThrowableProblem { - - private static final long serialVersionUID = 1L; - - public InternalServerErrorException(String message) { - super(ErrorConstants.DEFAULT_TYPE, message, Status.INTERNAL_SERVER_ERROR); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/InvalidPasswordException.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/InvalidPasswordException.java deleted file mode 100644 index e04b0b98..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/InvalidPasswordException.java +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import org.zalando.problem.AbstractThrowableProblem; -import org.zalando.problem.Status; - -public class InvalidPasswordException extends AbstractThrowableProblem { - - private static final long serialVersionUID = 1L; - - public InvalidPasswordException() { - super(ErrorConstants.INVALID_PASSWORD_TYPE, "Incorrect password", Status.BAD_REQUEST); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/LoginAlreadyUsedException.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/LoginAlreadyUsedException.java deleted file mode 100644 index 44d82faf..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/LoginAlreadyUsedException.java +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -public class LoginAlreadyUsedException extends BadRequestAlertException { - - private static final long serialVersionUID = 1L; - - public LoginAlreadyUsedException() { - super(ErrorConstants.LOGIN_ALREADY_USED_TYPE, "Login name already used!", "userManagement", "userexists"); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/package-info.java b/src/main/java/org/hostsharing/hsadminng/web/rest/errors/package-info.java deleted file mode 100644 index a3c8f2dd..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/errors/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Specific errors used with Zalando's "problem-spring-web" library. - * - * More information on https://github.com/zalando/problem-spring-web - */ -package org.hostsharing.hsadminng.web.rest.errors; diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/package-info.java b/src/main/java/org/hostsharing/hsadminng/web/rest/package-info.java deleted file mode 100644 index d08178fd..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Spring MVC REST controllers. - */ -package org.hostsharing.hsadminng.web.rest; diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/util/HeaderUtil.java b/src/main/java/org/hostsharing/hsadminng/web/rest/util/HeaderUtil.java deleted file mode 100644 index 3771468d..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/util/HeaderUtil.java +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; - -/** - * Utility class for HTTP headers creation. - */ -public final class HeaderUtil { - - private static final Logger log = LoggerFactory.getLogger(HeaderUtil.class); - - private static final String APPLICATION_NAME = "hsadminNgApp"; - - private HeaderUtil() { - } - - public static HttpHeaders createAlert(String message, String param) { - HttpHeaders headers = new HttpHeaders(); - headers.add("X-" + APPLICATION_NAME + "-alert", message); - headers.add("X-" + APPLICATION_NAME + "-params", param); - return headers; - } - - public static HttpHeaders createEntityCreationAlert(String entityName, String param) { - return createAlert(APPLICATION_NAME + "." + entityName + ".created", param); - } - - public static HttpHeaders createEntityUpdateAlert(String entityName, String param) { - return createAlert(APPLICATION_NAME + "." + entityName + ".updated", param); - } - - public static HttpHeaders createEntityDeletionAlert(String entityName, String param) { - return createAlert(APPLICATION_NAME + "." + entityName + ".deleted", param); - } - - public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) { - log.error("Entity processing failed, {}", defaultMessage); - HttpHeaders headers = new HttpHeaders(); - headers.add("X-" + APPLICATION_NAME + "-error", "error." + errorKey); - headers.add("X-" + APPLICATION_NAME + "-params", entityName); - return headers; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/util/PaginationUtil.java b/src/main/java/org/hostsharing/hsadminng/web/rest/util/PaginationUtil.java deleted file mode 100644 index bcd04789..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/util/PaginationUtil.java +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.util; - -import org.springframework.data.domain.Page; -import org.springframework.http.HttpHeaders; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * Utility class for handling pagination. - * - *

- * Pagination uses the same principles as the GitHub API, - * and follow RFC 5988 (Link header). - */ -public final class PaginationUtil { - - private PaginationUtil() { - } - - public static HttpHeaders generatePaginationHttpHeaders(Page page, String baseUrl) { - - HttpHeaders headers = new HttpHeaders(); - headers.add("X-Total-Count", Long.toString(page.getTotalElements())); - String link = ""; - if ((page.getNumber() + 1) < page.getTotalPages()) { - link = "<" + generateUri(baseUrl, page.getNumber() + 1, page.getSize()) + ">; rel=\"next\","; - } - // prev link - if ((page.getNumber()) > 0) { - link += "<" + generateUri(baseUrl, page.getNumber() - 1, page.getSize()) + ">; rel=\"prev\","; - } - // last and first link - int lastPage = 0; - if (page.getTotalPages() > 0) { - lastPage = page.getTotalPages() - 1; - } - link += "<" + generateUri(baseUrl, lastPage, page.getSize()) + ">; rel=\"last\","; - link += "<" + generateUri(baseUrl, 0, page.getSize()) + ">; rel=\"first\""; - headers.add(HttpHeaders.LINK, link); - return headers; - } - - private static String generateUri(String baseUrl, int page, int size) { - return UriComponentsBuilder.fromUriString(baseUrl).queryParam("page", page).queryParam("size", size).toUriString(); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/KeyAndPasswordVM.java b/src/main/java/org/hostsharing/hsadminng/web/rest/vm/KeyAndPasswordVM.java deleted file mode 100644 index 8d85d8ef..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/KeyAndPasswordVM.java +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.vm; - -/** - * View Model object for storing the user's key and password. - */ -public class KeyAndPasswordVM { - - private String key; - - private String newPassword; - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getNewPassword() { - return newPassword; - } - - public void setNewPassword(String newPassword) { - this.newPassword = newPassword; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/LoggerVM.java b/src/main/java/org/hostsharing/hsadminng/web/rest/vm/LoggerVM.java deleted file mode 100644 index edc032c2..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/LoggerVM.java +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.vm; - -import ch.qos.logback.classic.Logger; - -/** - * View Model object for storing a Logback logger. - */ -public class LoggerVM { - - private String name; - - private String level; - - public LoggerVM(Logger logger) { - this.name = logger.getName(); - this.level = logger.getEffectiveLevel().toString(); - } - - public LoggerVM() { - // Empty public constructor used by Jackson. - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getLevel() { - return level; - } - - public void setLevel(String level) { - this.level = level; - } - - @Override - public String toString() { - return "LoggerVM{" + - "name='" + name + '\'' + - ", level='" + level + '\'' + - '}'; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/LoginVM.java b/src/main/java/org/hostsharing/hsadminng/web/rest/vm/LoginVM.java deleted file mode 100644 index 951c7d46..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/LoginVM.java +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.vm; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * View Model object for storing a user's credentials. - */ -public class LoginVM { - - @NotNull - @Size(min = 1, max = 50) - private String username; - - @NotNull - @Size(min = ManagedUserVM.PASSWORD_MIN_LENGTH, max = ManagedUserVM.PASSWORD_MAX_LENGTH) - private String password; - - private Boolean rememberMe; - - 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 isRememberMe() { - return rememberMe; - } - - public void setRememberMe(Boolean rememberMe) { - this.rememberMe = rememberMe; - } - - @Override - public String toString() { - return "LoginVM{" + - "username='" + username + '\'' + - ", rememberMe=" + rememberMe + - '}'; - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/ManagedUserVM.java b/src/main/java/org/hostsharing/hsadminng/web/rest/vm/ManagedUserVM.java deleted file mode 100644 index 08005ceb..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/ManagedUserVM.java +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.vm; - -import org.hostsharing.hsadminng.service.dto.UserDTO; - -import javax.validation.constraints.Size; - -/** - * View Model extending the UserDTO, which is meant to be used in the user management UI. - */ -public class ManagedUserVM extends UserDTO { - - public static final int PASSWORD_MIN_LENGTH = 4; - - public static final int PASSWORD_MAX_LENGTH = 100; - - @Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH) - private String password; - - public ManagedUserVM() { - // Empty constructor needed for Jackson. - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - @Override - public String toString() { - return "ManagedUserVM{" + - "} " + super.toString(); - } -} diff --git a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/package-info.java b/src/main/java/org/hostsharing/hsadminng/web/rest/vm/package-info.java deleted file mode 100644 index 1659f464..00000000 --- a/src/main/java/org/hostsharing/hsadminng/web/rest/vm/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * View Models used by Spring MVC REST controllers. - */ -package org.hostsharing.hsadminng.web.rest.vm; diff --git a/src/main/jdl/accessrights.jdl b/src/main/jdl/accessrights.jdl deleted file mode 100644 index cd640ce1..00000000 --- a/src/main/jdl/accessrights.jdl +++ /dev/null @@ -1,23 +0,0 @@ -filter all -paginate all with infinite-scroll - -enum UserRole { - HOSTMASTER, - ADMIN, - SUPPORTER, - CONTRACTUAL_CONTACT, - FINANCIAL_CONTACT, - TECHNICAL_CONTACT, - CUSTOMER_USER -} - -entity UserRoleAssignment { - entityTypeId String required maxlength(32), - entityObjectId Long required, - assignedRole UserRole required -} - - -relationship ManyToOne { - UserRoleAssignment{user(login)} to User{required}, -} diff --git a/src/main/jdl/customer.jdl b/src/main/jdl/customer.jdl deleted file mode 100644 index 3cb5a6ba..00000000 --- a/src/main/jdl/customer.jdl +++ /dev/null @@ -1,90 +0,0 @@ -filter all -dto all with mapstruct -service all with serviceClass -paginate all with infinite-scroll - -enum CustomerKind { - NATURAL, - LEGAL -} - -enum VatRegion { - DOMESTIC, - EU, - OTHER -} - -entity Customer { - reference Integer required unique min(10000) max(99999), - prefix String required maxlength(3) unique pattern(/[a-z][a-z0-9]+/), - name String required maxlength(80), - kind CustomerKind required, - birthDate LocalDate, - birthPlace String maxlength(80), - registrationCourt String maxlength(80), - registrationNumber String maxlength(80), - vatRegion VatRegion required, - vatNumber String maxlength(40), - contractualSalutation String maxlength(80), - contractualAddress String required maxlength(400), - billingSalutation String maxlength(80), - billingAddress String maxlength(400), - remark String maxlength(160) -} - -entity Membership { - admissionDocumentDate LocalDate required, - cancellationDocumentDate LocalDate, - memberFromDate LocalDate required, - memberUntilDate LocalDate, - remark String maxlength(160) -} - -enum ShareAction { - SUBSCRIPTION, - CANCELLATION -} - -entity Share { - documentDate LocalDate required, - valueDate LocalDate required, - action ShareAction required, - quantity Integer required, - remark String maxlength(160) -} - -enum AssetAction { - PAYMENT, - HANDOVER, - ADOPTION, - LOSS, - CLEARING, - PAYBACK -} - -entity Asset { - documentDate LocalDate required, - valueDate LocalDate required, - action AssetAction required, - amount BigDecimal required, - remark String maxlength(160) -} - -entity SepaMandate { - reference String maxlength(40) unique required, - iban String maxlength(34), - bic String maxlength(11), - grantingDocumentDate LocalDate required, - revokationDocumentDate LocalDate, - validFromDate LocalDate required, - validUntilDate LocalDate, - lastUsedDate LocalDate, - remark String maxlength(160) -} - -relationship OneToMany { - Customer{membership} to Membership{customer(prefix) required}, - Customer{sepamandate} to SepaMandate{customer(prefix) required}, - Membership{share} to Share{membership(admissionDocumentDate) required}, - Membership{asset} to Asset{membership(admissionDocumentDate) required} -} diff --git a/src/main/jib/entrypoint.sh b/src/main/jib/entrypoint.sh deleted file mode 100644 index c395308d..00000000 --- a/src/main/jib/entrypoint.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP} -exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -cp /app/resources/:/app/classes/:/app/libs/* "org.hostsharing.hsadminng.HsadminNgApp" "$@" diff --git a/src/main/resources/.h2.server.properties b/src/main/resources/.h2.server.properties deleted file mode 100644 index b775c018..00000000 --- a/src/main/resources/.h2.server.properties +++ /dev/null @@ -1,6 +0,0 @@ -#H2 Server Properties -#Thu Apr 25 12:42:42 CEST 2019 -0=JHipster H2 (Memory)|org.h2.Driver|jdbc\:h2\:mem\:hsadminng|hsadminNg -webAllowOthers=true -webPort=8082 -webSSL=false diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt deleted file mode 100644 index e0bc55aa..00000000 --- a/src/main/resources/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ - - ${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗ - ${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗ - ${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╝ ╚█████╗ ██║ ██████╗ ███████╔╝ - ${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔═══██║ ██║ ██╔════╝ ╚═══██╗ ██║ ██╔═══╝ ██╔══██║ - ${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗ - ${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝ - -${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: -:: https://www.jhipster.tech ::${AnsiColor.DEFAULT} diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml deleted file mode 100644 index 8a2c6e53..00000000 --- a/src/main/resources/config/application-dev.yml +++ /dev/null @@ -1,117 +0,0 @@ -# =================================================================== -# Spring Boot configuration for the "dev" profile. -# -# This configuration overrides the application.yml file. -# -# More information on profiles: https://www.jhipster.tech/profiles/ -# More information on configuration properties: https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -# =================================================================== -# Standard Spring Boot properties. -# Full reference is available at: -# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html -# =================================================================== - -logging: - level: - ROOT: DEBUG - io.github.jhipster: DEBUG - org.hostsharing.hsadminng: DEBUG - -spring: - profiles: - active: dev - include: - - swagger - # Uncomment to activate TLS for the dev profile - #- tls - devtools: - restart: - enabled: true - additional-exclude: .h2.server.properties - livereload: - enabled: false # we use Webpack dev server + BrowserSync for livereload - jackson: - serialization: - indent-output: true - datasource: - # this is just a common configuration for the dev-profiles h2mem, h2file and pgsql - type: com.zaxxer.hikari.HikariDataSource - hikari: - poolName: Hikari - auto-commit: false - jpa: - show-sql: true - properties: - hibernate.id.new_generator_mappings: true - hibernate.connection.provider_disables_autocommit: true - hibernate.cache.use_second_level_cache: false - hibernate.cache.use_query_cache: false - hibernate.generate_statistics: true - mail: - host: localhost - port: 25 - username: - password: - messages: - cache-duration: PT1S # 1 second, see the ISO 8601 standard - thymeleaf: - cache: false - -server: - port: 8080 - -# =================================================================== -# JHipster specific properties -# -# Full reference is available at: https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -jhipster: - http: - version: V_1_1 # To use HTTP/2 you will need to activate TLS (see application-tls.yml) - cache: # Cache configuration - ehcache: # Ehcache configuration - time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache - max-entries: 100 # Number of objects in each cache entry - # CORS is only enabled by default with the "dev" profile, so BrowserSync can access the API - cors: - allowed-origins: "*" - allowed-methods: "*" - allowed-headers: "*" - exposed-headers: "Authorization,Link,X-Total-Count" - allow-credentials: true - max-age: 1800 - security: - authentication: - jwt: - # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one) - base64-secret: ZDFlMDUzODIzMTUzZDEwZjExN2E5ZjAzY2VhZmYzNDE1YjhlYWUxZGRhMGU3ODZiNjRkNjVlNzEwZjExYWY4YzczM2NlYzI5YWE1OTRkNWM0YThlYjZjZjA5Zjc5YWJkOTgzYjdhZjQxZWQyZGUyYjFlYjI5ZDE3NmE4M2UzYjQ= - # Token is valid 24 hours - token-validity-in-seconds: 86400 - token-validity-in-seconds-for-remember-me: 2592000 - mail: # specific JHipster mail property, for standard properties see MailProperties - from: hsadminNg@localhost - base-url: http://127.0.0.1:8080 - metrics: - logs: # Reports metrics in the logs - enabled: false - report-frequency: 60 # in seconds - logging: - logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration - enabled: false - host: localhost - port: 5000 - queue-size: 512 - -# =================================================================== -# Application specific properties -# Add your own application properties here, see the ApplicationProperties class -# to have type-safe configuration, like in the JHipsterProperties above -# -# More documentation is available at: -# https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -# application: diff --git a/src/main/resources/config/application-h2file.yml b/src/main/resources/config/application-h2file.yml deleted file mode 100644 index 6f13aad7..00000000 --- a/src/main/resources/config/application-h2file.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Configuration for the dev-profile using a file-based H2 database. - -spring: - profiles: - active: h2,h2file - include: - - dev - - swagger - # Uncomment to activate TLS for the dev profile - #- tls - datasource: - url: url: jdbc:h2:~/.hsadminng.h2db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE - username: hsadminNg - password: - h2: - console: - enabled: false - jpa: - database-platform: io.github.jhipster.domain.util.FixedH2Dialect - database: H2 diff --git a/src/main/resources/config/application-h2mem.yml b/src/main/resources/config/application-h2mem.yml deleted file mode 100644 index 8762de24..00000000 --- a/src/main/resources/config/application-h2mem.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Configuration for the dev-profile using an in-memory H2 database. - -spring: - profiles: - active: h2,h2mem - include: - - dev - - swagger - # Uncomment to activate TLS for the dev profile - #- tls - datasource: - url: jdbc:h2:mem:hsadminng;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE - username: hsadminNg - password: - h2: - console: - enabled: false - jpa: - database-platform: io.github.jhipster.domain.util.FixedH2Dialect - database: H2 - liquibase: - contexts: dev,sample-data diff --git a/src/main/resources/config/application-pgsql.yml b/src/main/resources/config/application-pgsql.yml deleted file mode 100644 index b4faca72..00000000 --- a/src/main/resources/config/application-pgsql.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Configuration for the dev-profile using a Postgres database specified via environment. - -spring: - profiles: - active: dev,pgsql - include: - - dev - - swagger - # Uncomment to activate TLS for the dev profile - #- tls - datasource: - url: ${HSADMINNG_DB_URL} - username: ${HSADMINNG_DB_USER} - password: ${HSADMINNG_DB_PASS} - jpa: - database-platform: io.github.jhipster.domain.util.FixedPostgreSQL82Dialect - database: POSTGRESQL diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml deleted file mode 100644 index 6d39439b..00000000 --- a/src/main/resources/config/application-prod.yml +++ /dev/null @@ -1,132 +0,0 @@ -# =================================================================== -# Spring Boot configuration for the "prod" profile. -# -# This configuration overrides the application.yml file. -# -# More information on profiles: https://www.jhipster.tech/profiles/ -# More information on configuration properties: https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -# =================================================================== -# Standard Spring Boot properties. -# Full reference is available at: -# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html -# =================================================================== - -logging: - level: - ROOT: INFO - org.hostsharing.hsadminng: INFO - io.github.jhipster: INFO - -spring: - devtools: - restart: - enabled: false - livereload: - enabled: false - datasource: - type: com.zaxxer.hikari.HikariDataSource - url: jdbc:postgresql://localhost:5432/hsadminNg - username: hsadminNg - password: - hikari: - poolName: Hikari - auto-commit: false - jpa: - database-platform: io.github.jhipster.domain.util.FixedPostgreSQL82Dialect - database: POSTGRESQL - show-sql: false - properties: - hibernate.id.new_generator_mappings: true - hibernate.connection.provider_disables_autocommit: true - hibernate.cache.use_second_level_cache: false - hibernate.cache.use_query_cache: false - hibernate.generate_statistics: true - liquibase: - contexts: prod - mail: - host: localhost - port: 25 - username: - password: - thymeleaf: - cache: true - -# =================================================================== -# To enable TLS in production, generate a certificate using: -# keytool -genkey -alias hsadminng -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 -# -# You can also use Let's Encrypt: -# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm -# -# Then, modify the server.ssl properties so your "server" configuration looks like: -# -# server: -# port: 443 -# ssl: -# key-store: classpath:config/tls/keystore.p12 -# key-store-password: password -# key-store-type: PKCS12 -# key-alias: hsadminng -# # The ciphers suite enforce the security by deactivating some old and deprecated SSL cipher, this list was tested against SSL Labs (https://www.ssllabs.com/ssltest/) -# ciphers: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,TLS_RSA_WITH_CAMELLIA_128_CBC_SHA -# =================================================================== -server: - port: 8080 - compression: - enabled: true - mime-types: text/html,text/xml,text/plain,text/css, application/javascript, application/json - min-response-size: 1024 - -# =================================================================== -# JHipster specific properties -# -# Full reference is available at: https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -jhipster: - http: - version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration) - cache: # Used by the CachingHttpHeadersFilter - timeToLiveInDays: 1461 - cache: # Cache configuration - ehcache: # Ehcache configuration - time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache - max-entries: 1000 # Number of objects in each cache entry - security: - authentication: - jwt: - # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one) - # As this is the PRODUCTION configuration, you MUST change the default key, and store it securely: - # - In the JHipster Registry (which includes a Spring Cloud Config server) - # - In a separate `application-prod.yml` file, in the same folder as your executable WAR file - # - In the `JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64_SECRET` environment variable - base64-secret: ZDFlMDUzODIzMTUzZDEwZjExN2E5ZjAzY2VhZmYzNDE1YjhlYWUxZGRhMGU3ODZiNjRkNjVlNzEwZjExYWY4YzczM2NlYzI5YWE1OTRkNWM0YThlYjZjZjA5Zjc5YWJkOTgzYjdhZjQxZWQyZGUyYjFlYjI5ZDE3NmE4M2UzYjQ= - # Token is valid 24 hours - token-validity-in-seconds: 86400 - token-validity-in-seconds-for-remember-me: 2592000 - mail: # specific JHipster mail property, for standard properties see MailProperties - from: hsadminNg@localhost - base-url: http://my-server-url-to-change # Modify according to your server's URL - metrics: - logs: # Reports metrics in the logs - enabled: false - report-frequency: 60 # in seconds - logging: - logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration - enabled: false - host: localhost - port: 5000 - queue-size: 512 - -# =================================================================== -# Application specific properties -# Add your own application properties here, see the ApplicationProperties class -# to have type-safe configuration, like in the JHipsterProperties above -# -# More documentation is available at: -# https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -# application: diff --git a/src/main/resources/config/application-tls.yml b/src/main/resources/config/application-tls.yml deleted file mode 100644 index c4e0565c..00000000 --- a/src/main/resources/config/application-tls.yml +++ /dev/null @@ -1,20 +0,0 @@ -# =================================================================== -# Activate this profile to enable TLS and HTTP/2. -# -# JHipster has generated a self-signed certificate, which will be used to encrypt traffic. -# As your browser will not understand this certificate, you will need to import it. -# -# Another (easiest) solution with Chrome is to enable the "allow-insecure-localhost" flag -# at chrome://flags/#allow-insecure-localhost -# =================================================================== -server: - ssl: - key-store: classpath:config/tls/keystore.p12 - key-store-password: password - key-store-type: PKCS12 - key-alias: selfsigned - ciphers: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 - enabled-protocols: TLSv1.2 -jhipster: - http: - version: V_2_0 diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml deleted file mode 100644 index a1d5ea68..00000000 --- a/src/main/resources/config/application.yml +++ /dev/null @@ -1,143 +0,0 @@ -# =================================================================== -# Spring Boot configuration. -# -# This configuration will be overridden by the Spring profile you use, -# for example application-dev.yml if you use the "dev" profile. -# -# More information on profiles: https://www.jhipster.tech/profiles/ -# More information on configuration properties: https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -# =================================================================== -# Standard Spring Boot properties. -# Full reference is available at: -# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html -# =================================================================== - -management: - endpoints: - web: - base-path: /management - exposure: - include: ["configprops", "env", "health", "info", "threaddump", "logfile", "jhi-metrics", "prometheus" ] - endpoint: - health: - show-details: when-authorized - jhi-metrics: - enabled: true - info: - git: - mode: full - health: - mail: - enabled: false # When using the MailService, configure an SMTP server and set this to true - metrics: - export: - # Prometheus is the default metrics backend - prometheus: - enabled: true - step: 60 - binders: - jvm: - enabled: true - processor: - enabled: true - uptime: - enabled: true - logback: - enabled: true - files: - enabled: true - integration: - enabled: true - distribution: - percentiles-histogram: - all: true - percentiles: - all: 0, 0.5, 0.75, 0.95, 0.99, 1.0 - web: - server: - auto-time-requests: true - -spring: - application: - name: hsadminNg - profiles: - # The commented value for `active` can be replaced with valid Spring profiles to load. - # Otherwise, it will be filled in by gradle when building the WAR file - # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS` - active: #spring.profiles.active# - jpa: - open-in-view: false - properties: - hibernate.jdbc.time_zone: UTC - hibernate: - ddl-auto: none - naming: - physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy - implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy - messages: - basename: i18n/messages - mvc: - favicon: - enabled: false - thymeleaf: - mode: HTML - jackson: - deserialization: - USE_BIG_DECIMAL_FOR_FLOATS: true - -server: - servlet: - session: - cookie: - http-only: true - -# Properties to be exposed on the /info management endpoint -info: - # Comma separated list of profiles that will trigger the ribbon to show - display-ribbon-on-profiles: "dev" - -# =================================================================== -# JHipster specific properties -# -# Full reference is available at: https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -jhipster: - async: - core-pool-size: 2 - max-pool-size: 50 - queue-capacity: 10000 - # By default CORS is disabled. Uncomment to enable. - #cors: - #allowed-origins: "*" - #allowed-methods: "*" - #allowed-headers: "*" - #exposed-headers: "Authorization,Link,X-Total-Count" - #allow-credentials: true - #max-age: 1800 - mail: - from: hsadminNg@localhost - swagger: - default-include-pattern: /api/.* - title: hsadminNg API - description: hsadminNg API documentation - version: 0.0.1 - terms-of-service-url: - contact-name: - contact-url: - contact-email: - license: - license-url: - -# =================================================================== -# Application specific properties -# Add your own application properties here, see the ApplicationProperties class -# to have type-safe configuration, like in the JHipsterProperties above -# -# More documentation is available at: -# https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -# application: diff --git a/src/main/resources/config/liquibase/authorities.csv b/src/main/resources/config/liquibase/authorities.csv deleted file mode 100644 index f56c3aa4..00000000 --- a/src/main/resources/config/liquibase/authorities.csv +++ /dev/null @@ -1,5 +0,0 @@ -name -ROLE_HOSTMASTER -ROLE_ADMIN -ROLE_SUPPORTER -ROLE_USER diff --git a/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml b/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml deleted file mode 100644 index ee13f6f6..00000000 --- a/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105332_added_entity_Customer.xml b/src/main/resources/config/liquibase/changelog/20190507105332_added_entity_Customer.xml deleted file mode 100644 index 678deeaa..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105332_added_entity_Customer.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105333_added_entity_Membership.xml b/src/main/resources/config/liquibase/changelog/20190507105333_added_entity_Membership.xml deleted file mode 100644 index b3f71ecc..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105333_added_entity_Membership.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105333_added_entity_constraints_Membership.xml b/src/main/resources/config/liquibase/changelog/20190507105333_added_entity_constraints_Membership.xml deleted file mode 100644 index aa5bb247..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105333_added_entity_constraints_Membership.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105334_added_entity_Share.xml b/src/main/resources/config/liquibase/changelog/20190507105334_added_entity_Share.xml deleted file mode 100644 index bfca511e..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105334_added_entity_Share.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105334_added_entity_constraints_Share.xml b/src/main/resources/config/liquibase/changelog/20190507105334_added_entity_constraints_Share.xml deleted file mode 100644 index ccfe0ddc..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105334_added_entity_constraints_Share.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105335_added_entity_Asset.xml b/src/main/resources/config/liquibase/changelog/20190507105335_added_entity_Asset.xml deleted file mode 100644 index d991554b..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105335_added_entity_Asset.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105335_added_entity_constraints_Asset.xml b/src/main/resources/config/liquibase/changelog/20190507105335_added_entity_constraints_Asset.xml deleted file mode 100644 index 2da4503b..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105335_added_entity_constraints_Asset.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105336_added_entity_SepaMandate.xml b/src/main/resources/config/liquibase/changelog/20190507105336_added_entity_SepaMandate.xml deleted file mode 100644 index 3bf94bdc..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105336_added_entity_SepaMandate.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105336_added_entity_constraints_SepaMandate.xml b/src/main/resources/config/liquibase/changelog/20190507105336_added_entity_constraints_SepaMandate.xml deleted file mode 100644 index 3975b2bc..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105336_added_entity_constraints_SepaMandate.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105342_added_entity_UserRoleAssignment.xml b/src/main/resources/config/liquibase/changelog/20190507105342_added_entity_UserRoleAssignment.xml deleted file mode 100644 index f56261bd..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105342_added_entity_UserRoleAssignment.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/20190507105342_added_entity_constraints_UserRoleAssignment.xml b/src/main/resources/config/liquibase/changelog/20190507105342_added_entity_constraints_UserRoleAssignment.xml deleted file mode 100644 index 1b8a1d14..00000000 --- a/src/main/resources/config/liquibase/changelog/20190507105342_added_entity_constraints_UserRoleAssignment.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/constraints_Membership.xml b/src/main/resources/config/liquibase/changelog/constraints_Membership.xml deleted file mode 100644 index 9ce63193..00000000 --- a/src/main/resources/config/liquibase/changelog/constraints_Membership.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/changelog/constraints_UserRoleAssignment.xml b/src/main/resources/config/liquibase/changelog/constraints_UserRoleAssignment.xml deleted file mode 100644 index 66a7712f..00000000 --- a/src/main/resources/config/liquibase/changelog/constraints_UserRoleAssignment.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/historicization/historicization.xml b/src/main/resources/config/liquibase/historicization/historicization.xml deleted file mode 100644 index 9f5f515d..00000000 --- a/src/main/resources/config/liquibase/historicization/historicization.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - CREATE OR REPLACE FUNCTION historicize() - RETURNS trigger - AS $$ - BEGIN - IF (TG_OP = 'INSERT') OR (TG_OP = 'UPDATE') THEN - EXECUTE 'INSERT INTO history VALUES (txid_current(), now()) ON CONFLICT DO NOTHING'; - EXECUTE format('INSERT INTO %I_history VALUES (DEFAULT, txid_current(), False, $1.*)', TG_TABLE_NAME) USING NEW; - RETURN NEW; - ELSE -- assuming TG_OP = 'DELETE' - EXECUTE 'INSERT INTO history VALUES (txid_current(), now()) ON CONFLICT DO NOTHING'; - EXECUTE format('INSERT INTO %I_history VALUES (DEFAULT, txid_current(), True, $1.*)', TG_TABLE_NAME) USING OLD; - RETURN OLD; - END IF; - END; - $$ - LANGUAGE plpgsql; - - - - - diff --git a/src/main/resources/config/liquibase/historicization/historicization_Asset.xml b/src/main/resources/config/liquibase/historicization/historicization_Asset.xml deleted file mode 100644 index 22584e19..00000000 --- a/src/main/resources/config/liquibase/historicization/historicization_Asset.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CREATE TRIGGER asset_historicize - AFTER INSERT OR DELETE OR UPDATE ON asset - FOR EACH ROW EXECUTE PROCEDURE historicize(); - - - DROP TRIGGER asset_historicize - - - - - - SELECT * - FROM asset_history - WHERE history_id IN ( - SELECT max(history_id) AS history_id - FROM asset_history - WHERE history_transaction <= current_setting('history.transaction')::bigint - GROUP BY id) -- assets are not deletable, thus id is enough - - - - - - diff --git a/src/main/resources/config/liquibase/historicization/historicization_Customer.xml b/src/main/resources/config/liquibase/historicization/historicization_Customer.xml deleted file mode 100644 index 6d7245fa..00000000 --- a/src/main/resources/config/liquibase/historicization/historicization_Customer.xml +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CREATE TRIGGER customer_historicize - AFTER INSERT OR DELETE OR UPDATE ON customer - FOR EACH ROW EXECUTE PROCEDURE historicize(); - - - DROP TRIGGER customer_historicize - - - - - - SELECT * - FROM customer_history - WHERE history_id IN ( - SELECT max(history_id) AS history_id - FROM customer_history - WHERE history_transaction <= current_setting('history.transaction')::bigint - GROUP BY name) -- must have a unique constraint - - - - diff --git a/src/main/resources/config/liquibase/historicization/historicization_Membership.xml b/src/main/resources/config/liquibase/historicization/historicization_Membership.xml deleted file mode 100644 index 9759f9d8..00000000 --- a/src/main/resources/config/liquibase/historicization/historicization_Membership.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CREATE TRIGGER membership_historicize - AFTER INSERT OR DELETE OR UPDATE ON membership - FOR EACH ROW EXECUTE PROCEDURE historicize(); - - - DROP TRIGGER membership_historicize - - - - - - SELECT * - FROM membership_history - WHERE history_id IN ( - SELECT max(history_id) AS history_id - FROM membership_history - WHERE history_transaction <= current_setting('history.transaction')::bigint - GROUP BY customer_id, member_from_date) -- must have a unique constraint - - - - diff --git a/src/main/resources/config/liquibase/historicization/historicization_SepaMandate.xml b/src/main/resources/config/liquibase/historicization/historicization_SepaMandate.xml deleted file mode 100644 index 7c15d4bf..00000000 --- a/src/main/resources/config/liquibase/historicization/historicization_SepaMandate.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CREATE TRIGGER sepa_mandate_historicize - AFTER INSERT OR DELETE OR UPDATE ON sepa_mandate - FOR EACH ROW EXECUTE PROCEDURE historicize(); - - - DROP TRIGGER sepa_mandate_historicize - - - - - - SELECT * - FROM sepa_mandate_history - WHERE history_id IN ( - SELECT max(history_id) AS history_id - FROM sepa_mandate_history - WHERE history_transaction <= current_setting('history.transaction')::bigint - GROUP BY reference) -- must have a unique constraint - - - - diff --git a/src/main/resources/config/liquibase/historicization/historicization_Share.xml b/src/main/resources/config/liquibase/historicization/historicization_Share.xml deleted file mode 100644 index 5f2219ff..00000000 --- a/src/main/resources/config/liquibase/historicization/historicization_Share.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CREATE TRIGGER share_historicize - AFTER INSERT OR DELETE OR UPDATE ON share - FOR EACH ROW EXECUTE PROCEDURE historicize(); - - - DROP TRIGGER share_historicize - - - - - - SELECT * - FROM share_history - WHERE history_id IN ( - SELECT max(history_id) AS history_id - FROM share_history - WHERE history_transaction <= current_setting('history.transaction')::bigint - GROUP BY id) -- shares are not deletable, thus id is enough - - - - - - diff --git a/src/main/resources/config/liquibase/historicization/historicization_User.xml b/src/main/resources/config/liquibase/historicization/historicization_User.xml deleted file mode 100644 index 7a8ffb63..00000000 --- a/src/main/resources/config/liquibase/historicization/historicization_User.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CREATE TRIGGER jhi_user_historicize - AFTER INSERT OR DELETE OR UPDATE ON jhi_user - FOR EACH ROW EXECUTE PROCEDURE historicize(); - - - DROP TRIGGER jhi_user_historicize - - - - - - SELECT * - FROM jhi_user_history - WHERE history_id IN ( - SELECT max(history_id) AS history_id - FROM jhi_user_history - WHERE history_transaction <= current_setting('history.transaction')::bigint - GROUP BY login) -- must have a unique constraint - - - - diff --git a/src/main/resources/config/liquibase/historicization/historicization_UserAuthority.xml b/src/main/resources/config/liquibase/historicization/historicization_UserAuthority.xml deleted file mode 100644 index fa896dc5..00000000 --- a/src/main/resources/config/liquibase/historicization/historicization_UserAuthority.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CREATE TRIGGER jhi_user_authority_historicize - AFTER INSERT OR DELETE OR UPDATE ON jhi_user_authority - FOR EACH ROW EXECUTE PROCEDURE historicize(); - - - DROP TRIGGER jhi_user_authority_historicize - - - - - - SELECT * - FROM jhi_user_authority_history - WHERE history_id IN ( - SELECT max(history_id) AS history_id - FROM jhi_user_authority_history - WHERE history_transaction <= current_setting('history.transaction')::bigint - GROUP BY user_id, authority_name) -- must have a unique constraint - - - - diff --git a/src/main/resources/config/liquibase/historicization/historicization_UserRoleAssignment.xml b/src/main/resources/config/liquibase/historicization/historicization_UserRoleAssignment.xml deleted file mode 100644 index 2f688580..00000000 --- a/src/main/resources/config/liquibase/historicization/historicization_UserRoleAssignment.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CREATE TRIGGER user_role_assignment_historicize - AFTER INSERT OR DELETE OR UPDATE ON user_role_assignment - FOR EACH ROW EXECUTE PROCEDURE historicize(); - - - DROP TRIGGER user_role_assignment_historicize - - - - - - SELECT * - FROM user_role_assignment_history - WHERE history_id IN ( - SELECT max(history_id) AS history_id - FROM user_role_assignment_history - WHERE history_transaction <= current_setting('history.transaction')::bigint - GROUP BY entity_type_id, entity_object_id, assigned_role, user_id) -- must have a unique constraint - - - - diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml deleted file mode 100644 index 60f9a193..00000000 --- a/src/main/resources/config/liquibase/master.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/sample-data/assets.csv b/src/main/resources/config/liquibase/sample-data/assets.csv deleted file mode 100644 index ac9d9efd..00000000 --- a/src/main/resources/config/liquibase/sample-data/assets.csv +++ /dev/null @@ -1,58 +0,0 @@ -id;membership_id;document_date;value_date;action;amount;remark -210001;1;2001-05-10;2001-05-11;PAYMENT;64;some|multiline|1. instalment|just to produce some more rows for tests :-) -210002;1;2001-05-10;2001-05-12;PAYMENT;64;some|multiline|2. instalment -210003;1;2001-05-10;2001-05-13;PAYMENT;64;some|multiline|3. instalment -210004;1;2001-05-10;2001-05-14;PAYMENT;64;some|multiline|4. instalment -210005;1;2001-05-10;2001-05-15;PAYMENT;64;some|multiline|5. instalment -210006;1;2001-05-10;2001-05-16;PAYMENT;64;some|multiline|6. instalment -210007;1;2001-05-10;2001-05-17;PAYMENT;64;some|multiline|7. instalment -210008;1;2001-05-10;2001-05-18;PAYMENT;64;some|multiline|8. instalment -210009;1;2001-05-10;2001-05-19;PAYMENT;64;some|multiline|9. instalment -210010;1;2001-05-10;2001-05-20;PAYMENT;64;some|multiline|10. instalment -210011;1;2001-05-10;2001-05-21;PAYMENT;64;some|multiline|11. instalment -210012;1;2001-05-10;2001-05-22;PAYMENT;64;some|multiline|12. instalment -210013;1;2001-05-10;2001-05-23;PAYMENT;64;some|multiline|13. instalment -210014;1;2001-05-10;2001-05-24;PAYMENT;64;some|multiline|14. instalment -210015;1;2001-05-10;2001-05-25;PAYMENT;64;some|multiline|15. instalment -210016;1;2001-05-10;2001-05-26;PAYMENT;64;some|multiline|16. instalment -210017;1;2007-09-09;2008-07-10;PAYBACK;512;1. instalment -210018;1;2007-09-09;2009-07-10;PAYBACK;512;2. instalment -210019;2;2017-02-15;2017-02-15;PAYMENT;512;became a member again -210020;2;2017-02-15;2017-02-15;PAYMENT;64;just writing something -210021;3;2003-06-11;2003-06-11;PAYMENT;256;some comment -210022;3;2003-06-15;2004-07-03;PAYBACK;256;cancelled membership -210023;4;2017-06-15;2017-05-17;PAYMENT;1024; -210024;5;2011-09-18;2011-09-01;PAYMENT;640; -210101;5;2013-01-15;2013-01-01;PAYMENT;64;signed more shares|1. installment -210102;5;2013-01-15;2013-01-02;PAYMENT;64;signed more shares|2. installment -210103;5;2013-01-15;2013-01-03;PAYMENT;64;signed more shares|3. installment -210104;5;2013-01-15;2013-01-04;PAYMENT;64;signed more shares|4. installment -210105;5;2013-01-15;2013-01-05;PAYMENT;64;signed more shares|5. installment -210106;5;2013-01-15;2013-01-06;PAYMENT;64;signed more shares|6. installment -210107;5;2013-01-15;2013-01-07;PAYMENT;64;signed more shares|7. installment -210108;5;2013-01-15;2013-01-08;PAYMENT;64;signed more shares|8. installment -210109;5;2013-01-15;2013-01-09;PAYMENT;64;signed more shares|9. installment -210110;5;2013-01-15;2013-01-10;PAYMENT;64;signed more shares|10. installment -210111;5;2013-01-15;2013-01-11;PAYMENT;64;signed more shares|11. installment -210112;5;2013-01-15;2013-01-12;PAYMENT;64;signed more shares|12. installment -210123;5;2013-01-15;2013-01-13;PAYMENT;64;signed more shares|13. installment -210124;5;2013-01-15;2013-01-14;PAYMENT;64;signed more shares|14. installment -210125;5;2013-01-15;2013-01-15;PAYMENT;64;signed more shares|15. installment -210126;5;2013-01-15;2013-01-16;PAYMENT;64;signed more shares|16. installment -210127;5;2013-01-15;2013-01-17;PAYMENT;64;signed more shares|17. installment -210128;5;2013-01-15;2013-01-18;PAYMENT;64;signed more shares|18. installment -210129;5;2013-01-15;2013-01-19;PAYMENT;64;signed more shares|19. installment -210130;5;2013-01-15;2013-01-20;PAYMENT;64;signed more shares|20. installment -210230;5;2017-12-22;2018-07-01;PAYBACK;160;cancelled most of their shares|1. installment|just to produce more rows for tests :-) -210231;5;2017-12-22;2018-07-02;PAYBACK;160;cancelled most of their shares|2. installment|just to produce more rows for tests :-) -210232;5;2017-12-22;2018-08-03;PAYBACK;160;cancelled most of their shares|3. installment|just to produce more rows for tests :-) -210233;5;2017-12-22;2018-09-04;PAYBACK;160;cancelled most of their shares|4. installment|just to produce more rows for tests :-) -210234;5;2017-12-22;2018-10-05;PAYBACK;160;cancelled most of their shares|5. installment|just to produce more rows for tests :-) -210235;5;2017-12-22;2018-11-06;PAYBACK;160;cancelled most of their shares|6. installment|just to produce more rows for tests :-) -210236;5;2017-12-22;2018-12-07;PAYBACK;160;cancelled most of their shares|7. installment|just to produce more rows for tests :-) -210237;5;2017-12-22;2019-01-08;PAYBACK;160;cancelled most of their shares|8. installment|just to produce more rows for tests :-) -210238;5;2017-12-22;2019-02-09;PAYBACK;160;cancelled most of their shares|9. installment|just to produce more rows for tests :-) -210239;5;2017-12-22;2019-03-10;PAYBACK;160;cancelled most of their shares|10. installment|just to produce more rows for tests :-) - - - diff --git a/src/main/resources/config/liquibase/sample-data/assets.xml b/src/main/resources/config/liquibase/sample-data/assets.xml deleted file mode 100644 index d880a765..00000000 --- a/src/main/resources/config/liquibase/sample-data/assets.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - DELETE FROM asset; - - - diff --git a/src/main/resources/config/liquibase/sample-data/customers.csv b/src/main/resources/config/liquibase/sample-data/customers.csv deleted file mode 100644 index 3db25e17..00000000 --- a/src/main/resources/config/liquibase/sample-data/customers.csv +++ /dev/null @@ -1,7 +0,0 @@ -id;reference;prefix;name;kind;vat_region;contractual_address -1;10001;aaa;AAA Corp.;LEGAL;DOMESTIC;Ballustrade 13|91334 Erlangen -2;10002;bbb;BBB GmbH;LEGAL;DOMESTIC;Irgendwo 0|11111 Berlin -3;10003;ccc;CCC AG;LEGAL;DOMESTIC;Haus 15|26539 Baltrum -4;10004;ddd;DDD eG;LEGAL;DOMESTIC;Industriestraße 98-102|n22999 Hamburg -5;10098;abc;ABC OHG;LEGAL;DOMESTIC;An der Weide 13|30123 Hannover -6;10099;bca;Bert Cäsar Adelbert;NATURAL;DOMESTIC;'Egerstraße 53|44225 Dortmund diff --git a/src/main/resources/config/liquibase/sample-data/customers.xml b/src/main/resources/config/liquibase/sample-data/customers.xml deleted file mode 100644 index 9ada85c7..00000000 --- a/src/main/resources/config/liquibase/sample-data/customers.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - DELETE FROM customer; - - - - diff --git a/src/main/resources/config/liquibase/sample-data/memberships.csv b/src/main/resources/config/liquibase/sample-data/memberships.csv deleted file mode 100644 index 14e43200..00000000 --- a/src/main/resources/config/liquibase/sample-data/memberships.csv +++ /dev/null @@ -1,7 +0,0 @@ -id;customer_id;admission_document_date;cancellation_document_date;member_from_date;member_until_date;remark -1;1;2001-04-10;2007-08-09;2001-04-11;2007-12-31;is now client of another member|see XYZ -2;1;2017-01-15;null;2017-01-15;null;nothing to say here -3;3;2003-05-11;2018-11-30;2003-05-15;2018-12-31;business discontinued -4;4;2017-05-15;null;2017-05-15;null;whatever|is|to|say -5;5;2011-08-18;null;2011-08-18;null;foo bar etc. - diff --git a/src/main/resources/config/liquibase/sample-data/memberships.xml b/src/main/resources/config/liquibase/sample-data/memberships.xml deleted file mode 100644 index 38bb4ca5..00000000 --- a/src/main/resources/config/liquibase/sample-data/memberships.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - DELETE FROM membership; - - - - diff --git a/src/main/resources/config/liquibase/sample-data/sepamandates.csv b/src/main/resources/config/liquibase/sample-data/sepamandates.csv deleted file mode 100644 index 2698cc95..00000000 --- a/src/main/resources/config/liquibase/sample-data/sepamandates.csv +++ /dev/null @@ -1,6 +0,0 @@ -id;customer_id;reference;iban;bic;granting_document_date;revokation_document_date;valid_from_date;valid_until_date;last_used_date;remark -1;1;DKXIDEHAC01;DE94500105172859877827;REF02039402;2018-01-15;null;2018-01-16;null;2019-04-09;a remark|over two lines -2;2;JKUIDEBIS00;DE56500105172321139153;REF2834823W;2017-06-03;2019-01-15;2017-06-04;2019-01-31;2019-01-10;for the old bank account -3;2;JUZTDEVER03;DE56500105172321139153;REF2834823W;2019-01-15;null;2019-02-01;null;2019-04-09;for the new bank account -4;3;CKIZDESiX98;DE24500105175933769123;REF23984928;2016-09-13;2018-11-20;2016-09-13;2018-12-31;2018-12-09;membership cancelled -5;5;ZUIJDEVOR12;DE92500105174781793571;REF23882384;2016-12-03;null;2016-12-03;null;2019-04-09;null diff --git a/src/main/resources/config/liquibase/sample-data/sepamandates.xml b/src/main/resources/config/liquibase/sample-data/sepamandates.xml deleted file mode 100644 index dee524a7..00000000 --- a/src/main/resources/config/liquibase/sample-data/sepamandates.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - DELETE FROM sepa_mandate; - - - - diff --git a/src/main/resources/config/liquibase/sample-data/shares.csv b/src/main/resources/config/liquibase/sample-data/shares.csv deleted file mode 100644 index b3cbe4e4..00000000 --- a/src/main/resources/config/liquibase/sample-data/shares.csv +++ /dev/null @@ -1,13 +0,0 @@ -id;membership_id;document_date;value_date;action;quantity;remark -1;1;2001-04-10;2001-04-11;SUBSCRIPTION;16;some|multiline|remark -2;1;2007-08-09;2007-12-31;CANCELLATION;16;another remark -3;2;2017-01-15;2017-01-17;SUBSCRIPTION;8;became a member again -4;2;2017-01-15;2017-01-17;SUBSCRIPTION;1;just writing something -5;3;2003-05-11;2003-05-18;SUBSCRIPTION;4;some comment -6;3;2003-05-15;2003-05-30;CANCELLATION;4;cancelled membership -7;4;2017-05-15;2017-05-17;SUBSCRIPTION;16; -8;5;2011-08-18;2011-09-01;SUBSCRIPTION;10; -9;5;2012-12-15;2013-01-01;SUBSCRIPTION;20;signed more shares -10;5;2017-11-22;2018-01-01;CANCELLATION;25;cancelled most of their shares - - diff --git a/src/main/resources/config/liquibase/sample-data/shares.xml b/src/main/resources/config/liquibase/sample-data/shares.xml deleted file mode 100644 index e19760b4..00000000 --- a/src/main/resources/config/liquibase/sample-data/shares.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - DELETE FROM share; - - - - diff --git a/src/main/resources/config/liquibase/sample-data/user_role_assignments.csv b/src/main/resources/config/liquibase/sample-data/user_role_assignments.csv deleted file mode 100644 index c601ed14..00000000 --- a/src/main/resources/config/liquibase/sample-data/user_role_assignments.csv +++ /dev/null @@ -1,17 +0,0 @@ -id;user_id;entity_type_id;entity_object_id;assigned_role -1001;111;customer.Customer;1;CONTRACTUAL_CONTACT -1002;112;customer.Customer;2;CONTRACTUAL_CONTACT -1003;113;customer.Customer;3;CONTRACTUAL_CONTACT -1004;114;customer.Customer;4;CONTRACTUAL_CONTACT -1005;115;customer.Customer;5;CONTRACTUAL_CONTACT -1006;116;customer.Customer;6;CONTRACTUAL_CONTACT -1007;120;customer.Customer;1;FINANCIAL_CONTACT -1008;121;customer.Customer;1;TECHNICAL_CONTACT -1009;122;customer.Customer;2;FINANCIAL_CONTACT -1010;123;customer.Customer;2;TECHNICAL_CONTACT -1011;124;customer.Customer;3;FINANCIAL_CONTACT -1012;125;customer.Customer;3;TECHNICAL_CONTACT -1013;126;customer.Customer;4;FINANCIAL_CONTACT -1014;127;customer.Customer;4;TECHNICAL_CONTACT -1015;128;customer.Customer;5;FINANCIAL_CONTACT -1016;129;customer.Customer;5;TECHNICAL_CONTACT diff --git a/src/main/resources/config/liquibase/sample-data/user_role_assignments.xml b/src/main/resources/config/liquibase/sample-data/user_role_assignments.xml deleted file mode 100644 index 79b9d1e0..00000000 --- a/src/main/resources/config/liquibase/sample-data/user_role_assignments.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - DELETE FROM user_role_assignment WHERE id >=100 AND id < 1000000; - - - diff --git a/src/main/resources/config/liquibase/sample-data/users.csv b/src/main/resources/config/liquibase/sample-data/users.csv deleted file mode 100644 index 7fc49fe9..00000000 --- a/src/main/resources/config/liquibase/sample-data/users.csv +++ /dev/null @@ -1,24 +0,0 @@ -id;login;password_hash;first_name;last_name;email;image_url;activated;lang_key;created_by;last_modified_by -101;test-supporter-a;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;A;Test Supporter;ts-a@example.com;;true;de;system;system -102;test-supporter-b;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;B;Test Supporter;ts-b@example.com;;true;de;system;system -103;test-supporter-c;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;C;Test Supporter;ts-c@example.com;;true;de;system;system -104;test-admin-a;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;A;Test Admin;ta-a@example.com;;true;de;system;system -105;test-admin-b;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;B;Test Admin;ta-b@example.com;;true;de;system;system -106;test-hostmaster-a;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;A;Test Hostmaster;th-a@example.com;;true;de;system;system -107;test-hostmaster-b;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;B;Test Hostmaster;th-b@example.com;;true;de;system;system -111;aaa-test;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Anton;AAA Testuser;aaa@example.com;;true;de;system;system -112;bbb-test;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Berta;BBB Testuser;bbb@example.com;;true;de;system;system -113;ccc-test;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Cecilie;CCC Testuser;ccc@example.com;;true;de;system;system -114;ddd-test;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Dieter;DDD Testuser;ddd@example.com;;true;de;system;system -115;abc-test;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Alva;ABC Testuser;abc@example.com;;true;de;system;system -116;bca-test;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Bert Cäsar;Adelbert;bca@example.com;;true;de;system;system -120;test00;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 00;Test-User;test-user00@localhost;;true;de;system;system -121;test01;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 01;Test-User;test-user01@localhost;;true;de;system;system -122;test02;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 02;Test-User;test-user02@localhost;;true;de;system;system -123;test03;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 03;Test-User;test-user03@localhost;;true;de;system;system -124;test04;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 04;Test-User;test-user04@localhost;;true;de;system;system -125;test05;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 05;Test-User;test-user05@localhost;;true;de;system;system -126;test06;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 06;Test-User;test-user06@localhost;;true;de;system;system -127;test07;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 07;Test-User;test-user07@localhost;;true;de;system;system -128;test08;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 08;Test-User;test-user08@localhost;;true;de;system;system -129;test09;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;Test-User 09;Test-User;test-user09@localhost;;true;de;system;system diff --git a/src/main/resources/config/liquibase/sample-data/users.xml b/src/main/resources/config/liquibase/sample-data/users.xml deleted file mode 100644 index f0e88f95..00000000 --- a/src/main/resources/config/liquibase/sample-data/users.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - DELETE FROM user WHERE id >=100 AND id < 1000000; - - - diff --git a/src/main/resources/config/liquibase/sample-data/users_authorities.csv b/src/main/resources/config/liquibase/sample-data/users_authorities.csv deleted file mode 100644 index 9ff0cc57..00000000 --- a/src/main/resources/config/liquibase/sample-data/users_authorities.csv +++ /dev/null @@ -1,33 +0,0 @@ -user_id;authority_name -101;ROLE_SUPPORTER -101;ROLE_USER -102;ROLE_SUPPORTER -102;ROLE_USER -103;ROLE_SUPPORTER -103;ROLE_USER -104;ROLE_ADMIN -104;ROLE_USER -105;ROLE_ADMIN -105;ROLE_USER -106;ROLE_HOSTMASTER -106;ROLE_ADMIN -106;ROLE_USER -107;ROLE_HOSTMASTER -107;ROLE_ADMIN -107;ROLE_USER -111;ROLE_USER -112;ROLE_USER -113;ROLE_USER -114;ROLE_USER -115;ROLE_USER -116;ROLE_USER -120;ROLE_USER -121;ROLE_USER -122;ROLE_USER -123;ROLE_USER -124;ROLE_USER -125;ROLE_USER -126;ROLE_USER -127;ROLE_USER -128;ROLE_USER -129;ROLE_USER diff --git a/src/main/resources/config/liquibase/sample-data/users_authorities.xml b/src/main/resources/config/liquibase/sample-data/users_authorities.xml deleted file mode 100644 index 11ac1f25..00000000 --- a/src/main/resources/config/liquibase/sample-data/users_authorities.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - DELETE FROM JHI_USER_AUTHORITY WHERE id >=100 AND id < 1000000; - - - diff --git a/src/main/resources/config/liquibase/users.csv b/src/main/resources/config/liquibase/users.csv deleted file mode 100644 index 55309a74..00000000 --- a/src/main/resources/config/liquibase/users.csv +++ /dev/null @@ -1,5 +0,0 @@ -id;login;password_hash;first_name;last_name;email;image_url;activated;lang_key;created_by;last_modified_by -1;system;$2a$10$mE.qmcV0mFU5NcKh73TZx.z4ueI/.bDWbj0T1BYyqP481kGGarKLG;System;System;system@localhost;;true;de;system;system -2;anonymoususer;$2a$10$j8S5d7Sr7.8VTOYNviDPOeWX8KcYILUVJBsYV83Y5NtECayypx9lO;Anonymous;User;anonymous@localhost;;true;de;system;system -3;admin;$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC;Administrator;Administrator;admin@localhost;;true;de;system;system -4;user;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;User;User;user@localhost;;true;de;system;system diff --git a/src/main/resources/config/liquibase/users_authorities.csv b/src/main/resources/config/liquibase/users_authorities.csv deleted file mode 100644 index bb482bfa..00000000 --- a/src/main/resources/config/liquibase/users_authorities.csv +++ /dev/null @@ -1,7 +0,0 @@ -user_id;authority_name -1;ROLE_HOSTMASTER -1;ROLE_ADMIN -1;ROLE_USER -3;ROLE_ADMIN -3;ROLE_USER -4;ROLE_USER diff --git a/src/main/resources/config/tls/keystore.p12 b/src/main/resources/config/tls/keystore.p12 deleted file mode 100644 index afd38f56634ade72169b5455ebcefc44269fc67c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2615 zcmY+EcQ_l08pac1#7KqMwd<%@2_jDHRS~s!j5cQN8kMTH2#u}ws8JL}(NHytQng#F zp=dewb!wEFwd%U}x%Zss{P8{S^S$r;eSiKCBqjwKAT5Ff-k=A=$yo9sE06(LL;}x( zNZ{!UdjdhC3;0Jw=L{mzIbGQJi?OC>{@)b~6Og8e1Uf;GK!*rPdZz!|7ti@YFncSt zq*zVRT_JEvy7M*2VR9QKjfNKR-V{Uv_3O^goiV2;3z60~sD{-a(3||{pwVQ7=z;1& z7DYG7!}uI8;#?`U7|QV9iqVV`0EPS$$^R?sW2ep+>z8YOO>33Da zm|uB$7)CXvx=Ps0eU=(LTAK>{?e)-pQ^kP9PY&~*SDsZNpwMKx`H;I9y4v|CB9h}2 zqf=hNo?lMFZK<}*`Q|uRW?%m)S$-w;-i?20qW*M;vPynW~pS$>JMw$V8IVKHnLakmWmAx|5kiEQDQWru!7wZw?ta&SqTA6n0A zr+6(8L1{WT)V^5&(Zm5fT)TkdJ7QBfHI~LzTK1MKh68rQFkR?s3`

LHOf-LBhvA z39YM$)T64aJIIh2*P z(!zDl;kUFgiJHvVMZ$+{(~_WU(XlB`cG|5$k+1q?u%5iL1Vf8Y_~9Ax>4bAFnJ%gnT(&dp_0iox6(z9 zYG*5mmGK^ zRyVJK1y6gFXZsh2Jn}q*?}fy?jGkSlMnguF5!(AcA!55Jkq`FZz;(iQM2PA|TV+Ye zKVvn8-YfL{88e&Brp<{eW9JePMKD;Z<%IwCcHyJq$;=yn-s>l!xQ#XnMEql<35hCq%wBgQjt_;YdsAZ#=gNw z>JR4@MAus`BD2T)>DJw~mEO*klh@sva?O_6iQG8p1m|~>$W7Ia61(|~#-pcm4|odC zi|42wf`X_M3XL#v)ak%ljor-kJa>shd^WSw%O;M@d39>-RTk}x`7(N1RRlB8{+V)5 zFD^L>h^qCFc!QsFd&YBXyWEhwc`9QhqJZ{@w7G66U_iAi``A-i z$4_$ka8l<$HwU*&O;Kuz-bESeLyw_1@F2o8%R?qlgTb-<-Lw^~?jPWK1;6J7^$G(l zIDArcybbR28lsh>jv?{D{xb+?8$k>b{JGOq#G5GH7MomjkBo-m| z7b?LpB$prz5Cm`o_yF85)Dz$V@VnSt5h8z=IFT?AyQQbEn=lfEQbZ!r@=8i*rQa_g z>hM>If&PN4Mi-U=4G?f4+J7g&e~^~`Kcrm|+Hdt_b$l_DNg1JYgb3eFqQW(6 zfcaG@Bb*gZS>GHJ{;fWjL09u|YSgIli~H7+4SxTcRqaH9@Hx&A~G5 z!EZIaVlrUaPFtHTiMDkw#oCPXyS&Y+96viV`Mrw6?pC)oU)1_o7yl`Rls@|6>?ih+ z9bT5K)_RwHqtpNg!C^yaPX_lldh)Y+jcSEd0QghGRLlaNd+hu8C_|-Ly|gQCw)ryB z`+Ipp08+hAlGRPIn!TbDmK#4bQ&e7kdG&h34MCL}TZd2=Ympe(@M}HO1x(zhR@V6Q zcVc@2O&)ro5O|mSUf{ph>L!2rwSq@=XCME$h0$0W!(D$YWK2Pkxo9pg__Q=##Uy$Q z6A|e?)vszEvWR2a^}A^pP2NjZ8_H#wKzJFQ84FCZ>b!)Y502yfPQzBKtWnO+OWJCD z^P)(XYhv9TISCnWtqo}jj&Y|cH}%X_0JRYsnB)Bfvz@re`yDAl*U7o)+I9Kg_RZS* zBcC-8IvWd$^rzTr)VMEeTi|lP`CdNmm3cg0G%qtsWiVK+P-5t*W$ zIdbiNTYjMBrc4gKlE=ed8*N%H+S$dr?-*67Qx#`o5qKI%XyN8wJo~Be#-5;*y0+rr8HuRa}r?nfdyJ|O?5EUn58O`bGK86 zZ3vXPp9Ju0T~cR%FpQ!oaSR7^Mk-tydnW*y82S{;5Nkk^hIc?Go_eticEaJddLF5D z2SNI5qx7qO{7yC8Mfto4eb4eA{5^6==$_g7uhdoTVEF(vFlLKJ53i}9Br59V6k$+y zW(aWF<5A7#Gj3JUd6v^FrsQzy+gTsb(AE$;u;pd=(fV*hL*@>J2kCCZL`~Z6dN0E? z=Hi_X8h8{D24j2vAv12TIn?Hj-fVq_R%^NP?a%=_Mt;-zAb_6JK1_!FPYI(aYT$pptRt)^-C9R=9o#nXeRN^Nut+df#{tAaC6 zuP84sLW5T>IqZrnh4oitZk2_%aYV}hl&>$GCIi|-_o7$Sxk~H&b#@Ty2oVIBo=yrv zOT$M80Kw?ku0<~!P(2p&3aCeq*ml}%yaPb6xRK$&7@;M*qawo}LBcTv^0OX68d@N^ F=-;@-rknr( diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties deleted file mode 100644 index d387d6bc..00000000 --- a/src/main/resources/i18n/messages.properties +++ /dev/null @@ -1,21 +0,0 @@ -# Error page -error.title=Your request cannot be processed -error.subtitle=Sorry, an error has occurred. -error.status=Status: -error.message=Message: - -# Activation email -email.activation.title=hsadminNg account activation -email.activation.greeting=Dear {0} -email.activation.text1=Your hsadminNg account has been created, please click on the URL below to activate it: -email.activation.text2=Regards, -email.signature=hsadminNg Team. - -# Creation email -email.creation.text1=Your hsadminNg account has been created, please click on the URL below to access it: - -# Reset email -email.reset.title=hsadminNg password reset -email.reset.greeting=Dear {0} -email.reset.text1=For your hsadminNg account a password reset was requested, please click on the URL below to reset it: -email.reset.text2=Regards, diff --git a/src/main/resources/i18n/messages_de.properties b/src/main/resources/i18n/messages_de.properties deleted file mode 100644 index ae2a4968..00000000 --- a/src/main/resources/i18n/messages_de.properties +++ /dev/null @@ -1,21 +0,0 @@ -# Error page -error.title=Ihre Anfrage kann nicht bearbeitet werden -error.subtitle=Verzeihung, ein Fehler ist aufgetreten. -error.status=Status: -error.message=Meldung: - -# Activation email -email.activation.title=hsadminNg Aktivierung -email.activation.greeting=Liebe(r) {0} -email.activation.text1=Ihr hsadminNg Zugang wurde angelegt, bitte klicken Sie unten auf den Link, um diesen zu aktivieren: -email.activation.text2=Grüße, -email.signature=hsadminNg. - -# Creation email -email.creation.text1=Ihr hsadminNg Zugang wurde angelegt, bitte klicken Sie auf den Link um sich anzumelden: - -# Reset email -email.reset.title=hsadminNg Passwort zurücksetzen -email.reset.greeting=Liebe(r) {0} -email.reset.text1=Für Ihren hsadminNg Account wurde ein neues Passwort angefordert, bitte klicken Sie unten auf den Link, um das Passwort zurückzusetzen: -email.reset.text2=Grüße, diff --git a/src/main/resources/i18n/messages_en.properties b/src/main/resources/i18n/messages_en.properties deleted file mode 100644 index d387d6bc..00000000 --- a/src/main/resources/i18n/messages_en.properties +++ /dev/null @@ -1,21 +0,0 @@ -# Error page -error.title=Your request cannot be processed -error.subtitle=Sorry, an error has occurred. -error.status=Status: -error.message=Message: - -# Activation email -email.activation.title=hsadminNg account activation -email.activation.greeting=Dear {0} -email.activation.text1=Your hsadminNg account has been created, please click on the URL below to activate it: -email.activation.text2=Regards, -email.signature=hsadminNg Team. - -# Creation email -email.creation.text1=Your hsadminNg account has been created, please click on the URL below to access it: - -# Reset email -email.reset.title=hsadminNg password reset -email.reset.greeting=Dear {0} -email.reset.text1=For your hsadminNg account a password reset was requested, please click on the URL below to reset it: -email.reset.text2=Regards, diff --git a/src/main/resources/idea.gdsl b/src/main/resources/idea.gdsl deleted file mode 100644 index dfe6b536..00000000 --- a/src/main/resources/idea.gdsl +++ /dev/null @@ -1,90 +0,0 @@ -// Jenkinsfile completions for Intellij IDEA - -def ctx = context(scope: scriptScope()) -contributor(ctx) { - method(name: 'build', type: 'Object', params: [job:'java.lang.String'], doc: 'Build a job') - method(name: 'build', type: 'Object', namedParams: [parameter(name: 'job', type: 'java.lang.String'), parameter(name: 'parameters', type: 'Map'), parameter(name: 'propagate', type: 'boolean'), parameter(name: 'quietPeriod', type: 'java.lang.Integer'), parameter(name: 'wait', type: 'boolean'), ], doc: 'Build a job') - method(name: 'echo', type: 'Object', params: [message:'java.lang.String'], doc: 'Print Message') - method(name: 'emailext', type: 'Object', namedParams: [parameter(name: 'subject', type: 'java.lang.String'), parameter(name: 'body', type: 'java.lang.String'), parameter(name: 'attachLog', type: 'boolean'), parameter(name: 'attachmentsPattern', type: 'java.lang.String'), parameter(name: 'compressLog', type: 'boolean'), parameter(name: 'mimeType', type: 'java.lang.String'), parameter(name: 'recipientProviders', type: 'Map'), parameter(name: 'replyTo', type: 'java.lang.String'), parameter(name: 'to', type: 'java.lang.String'), ], doc: 'Extended Email') - method(name: 'emailextrecipients', type: 'Object', params: [recipientProviders:'Map'], doc: 'Extended Email Recipients') - method(name: 'error', type: 'Object', params: [message:'java.lang.String'], doc: 'Error signal') - method(name: 'input', type: 'Object', params: [message:'java.lang.String'], doc: 'Wait for interactive input') - method(name: 'input', type: 'Object', namedParams: [parameter(name: 'message', type: 'java.lang.String'), parameter(name: 'id', type: 'java.lang.String'), parameter(name: 'ok', type: 'java.lang.String'), parameter(name: 'parameters', type: 'Map'), parameter(name: 'submitter', type: 'java.lang.String'), parameter(name: 'submitterParameter', type: 'java.lang.String'), ], doc: 'Wait for interactive input') - method(name: 'isUnix', type: 'Object', params: [:], doc: 'Checks if running on a Unix-like node') - method(name: 'library', type: 'Object', params: [identifier:'java.lang.String'], doc: 'Load a shared library on the fly') - method(name: 'library', type: 'Object', namedParams: [parameter(name: 'identifier', type: 'java.lang.String'), parameter(name: 'retriever', type: 'Map'), ], doc: 'Load a shared library on the fly') - method(name: 'libraryResource', type: 'Object', params: [resource:'java.lang.String'], doc: 'Load a resource file from a shared library') - method(name: 'mail', type: 'Object', namedParams: [parameter(name: 'subject', type: 'java.lang.String'), parameter(name: 'body', type: 'java.lang.String'), parameter(name: 'bcc', type: 'java.lang.String'), parameter(name: 'cc', type: 'java.lang.String'), parameter(name: 'charset', type: 'java.lang.String'), parameter(name: 'from', type: 'java.lang.String'), parameter(name: 'mimeType', type: 'java.lang.String'), parameter(name: 'replyTo', type: 'java.lang.String'), parameter(name: 'to', type: 'java.lang.String'), ], doc: 'Mail') - method(name: 'milestone', type: 'Object', params: [ordinal:'java.lang.Integer'], doc: 'The milestone step forces all builds to go through in order') - method(name: 'milestone', type: 'Object', namedParams: [parameter(name: 'ordinal', type: 'java.lang.Integer'), parameter(name: 'label', type: 'java.lang.String'), ], doc: 'The milestone step forces all builds to go through in order') - method(name: 'properties', type: 'Object', params: [properties:'Map'], doc: 'Set job properties') - method(name: 'readTrusted', type: 'Object', params: [path:'java.lang.String'], doc: 'Read trusted file from SCM') - method(name: 'resolveScm', type: 'Object', namedParams: [parameter(name: 'source', type: 'Map'), parameter(name: 'targets', type: 'Map'), parameter(name: 'ignoreErrors', type: 'boolean'), ], doc: 'Resolves an SCM from an SCM Source and a list of candidate target branch names') - method(name: 'retry', type: 'Object', params: [count:int, body:'Closure'], doc: 'Retry the body up to N times') - method(name: 'script', type: 'Object', params: [body:'Closure'], doc: 'Run arbitrary Pipeline script') - method(name: 'sleep', type: 'Object', params: [time:'int'], doc: 'Sleep') - method(name: 'sleep', type: 'Object', namedParams: [parameter(name: 'time', type: 'int'), parameter(name: 'unit', type: 'java.util.concurrent.TimeUnit'), ], doc: 'Sleep') - method(name: 'stage', type: 'Object', params: [name:java.lang.String, body:'Closure'], doc: 'Stage') - method(name: 'stage', type: 'Object', params: [body:Closure], namedParams: [parameter(name: 'name', type: 'java.lang.String'), parameter(name: 'concurrency', type: 'java.lang.Integer'), ], doc: 'Stage') - method(name: 'timeout', type: 'Object', params: [time:int, body:'Closure'], doc: 'Enforce time limit') - method(name: 'timeout', type: 'Object', params: [body:Closure], namedParams: [parameter(name: 'time', type: 'int'), parameter(name: 'unit', type: 'java.util.concurrent.TimeUnit'), ], doc: 'Enforce time limit') - method(name: 'timestamps', type: 'Object', params: [body:'Closure'], doc: 'Timestamps') - method(name: 'tool', type: 'Object', params: [name:'java.lang.String'], doc: 'Use a tool from a predefined Tool Installation') - method(name: 'tool', type: 'Object', namedParams: [parameter(name: 'name', type: 'java.lang.String'), parameter(name: 'type', type: 'java.lang.String'), ], doc: 'Use a tool from a predefined Tool Installation') - method(name: 'waitUntil', type: 'Object', params: [body:'Closure'], doc: 'Wait for condition') - method(name: 'withEnv', type: 'Object', params: [overrides:Map, body:'Closure'], doc: 'Set environment variables') - method(name: 'ws', type: 'Object', params: [dir:java.lang.String, body:'Closure'], doc: 'Allocate workspace') - method(name: 'catchError', type: 'Object', params: [body:'Closure'], doc: 'Advanced/Deprecated Catch error and set build result') - method(name: 'dockerFingerprintRun', type: 'Object', params: [containerId:'java.lang.String'], doc: 'Advanced/Deprecated Record trace of a Docker image run in a container') - method(name: 'dockerFingerprintRun', type: 'Object', namedParams: [parameter(name: 'containerId', type: 'java.lang.String'), parameter(name: 'toolName', type: 'java.lang.String'), ], doc: 'Record trace of a Docker image run in a container') - method(name: 'envVarsForTool', type: 'Object', namedParams: [parameter(name: 'toolId', type: 'java.lang.String'), parameter(name: 'toolVersion', type: 'java.lang.String'), ], doc: 'Fetches the environment variables for a given tool in a list of \'FOO=bar\' strings suitable for the withEnv step.') - method(name: 'getContext', type: 'Object', params: [type:'Map'], doc: 'Advanced/Deprecated Get contextual object from internal APIs') - method(name: 'withContext', type: 'Object', params: [context:java.lang.Object, body:'Closure'], doc: 'Advanced/Deprecated Use contextual object from internal APIs within a block') - property(name: 'docker', type: 'org.jenkinsci.plugins.docker.workflow.DockerDSL') - property(name: 'pipeline', type: 'org.jenkinsci.plugins.pipeline.modeldefinition.ModelStepLoader') - property(name: 'env', type: 'org.jenkinsci.plugins.workflow.cps.EnvActionImpl.Binder') - property(name: 'params', type: 'org.jenkinsci.plugins.workflow.cps.ParamsVariable') - property(name: 'currentBuild', type: 'org.jenkinsci.plugins.workflow.cps.RunWrapperBinder') - property(name: 'scm', type: 'org.jenkinsci.plugins.workflow.multibranch.SCMVar') -} -//Steps that require a node context -def nodeCtx = context(scope: closureScope()) -contributor(nodeCtx) { - def call = enclosingCall('node') - if (call) { - method(name: 'bat', type: 'Object', params: [script:'java.lang.String'], doc: 'Windows Batch Script') - method(name: 'bat', type: 'Object', namedParams: [parameter(name: 'script', type: 'java.lang.String'), parameter(name: 'encoding', type: 'java.lang.String'), parameter(name: 'returnStatus', type: 'boolean'), parameter(name: 'returnStdout', type: 'boolean'), ], doc: 'Windows Batch Script') - method(name: 'checkout', type: 'Object', params: [scm:'Map'], doc: 'General SCM') - method(name: 'checkout', type: 'Object', namedParams: [parameter(name: 'scm', type: 'Map'), parameter(name: 'changelog', type: 'boolean'), parameter(name: 'poll', type: 'boolean'), ], doc: 'General SCM') - method(name: 'deleteDir', type: 'Object', params: [:], doc: 'Recursively delete the current directory from the workspace') - method(name: 'dir', type: 'Object', params: [path:java.lang.String, body:'Closure'], doc: 'Change current directory') - method(name: 'fileExists', type: 'Object', params: [file:'java.lang.String'], doc: 'Verify if file exists in workspace') - method(name: 'git', type: 'Object', params: [url:'java.lang.String'], doc: 'Git') - method(name: 'git', type: 'Object', namedParams: [parameter(name: 'url', type: 'java.lang.String'), parameter(name: 'branch', type: 'java.lang.String'), parameter(name: 'changelog', type: 'boolean'), parameter(name: 'credentialsId', type: 'java.lang.String'), parameter(name: 'poll', type: 'boolean'), ], doc: 'Git') - method(name: 'load', type: 'Object', params: [path:'java.lang.String'], doc: 'Evaluate a Groovy source file into the Pipeline script') - method(name: 'pwd', type: 'Object', params: [:], doc: 'Determine current directory') - method(name: 'pwd', type: 'Object', namedParams: [parameter(name: 'tmp', type: 'boolean'), ], doc: 'Determine current directory') - method(name: 'readFile', type: 'Object', params: [file:'java.lang.String'], doc: 'Read file from workspace') - method(name: 'readFile', type: 'Object', namedParams: [parameter(name: 'file', type: 'java.lang.String'), parameter(name: 'encoding', type: 'java.lang.String'), ], doc: 'Read file from workspace') - method(name: 'sh', type: 'Object', params: [script:'java.lang.String'], doc: 'Shell Script') - method(name: 'sh', type: 'Object', namedParams: [parameter(name: 'script', type: 'java.lang.String'), parameter(name: 'encoding', type: 'java.lang.String'), parameter(name: 'returnStatus', type: 'boolean'), parameter(name: 'returnStdout', type: 'boolean'), ], doc: 'Shell Script') - method(name: 'stash', type: 'Object', params: [name:'java.lang.String'], doc: 'Stash some files to be used later in the build') - method(name: 'stash', type: 'Object', namedParams: [parameter(name: 'name', type: 'java.lang.String'), parameter(name: 'excludes', type: 'java.lang.String'), parameter(name: 'includes', type: 'java.lang.String'), parameter(name: 'useDefaultExcludes', type: 'boolean'), ], doc: 'Stash some files to be used later in the build') - method(name: 'step', type: 'Object', params: [delegate:'Map'], doc: 'General Build Step') - method(name: 'svn', type: 'Object', params: [url:'java.lang.String'], doc: 'Subversion') - method(name: 'svn', type: 'Object', namedParams: [parameter(name: 'url', type: 'java.lang.String'), parameter(name: 'changelog', type: 'boolean'), parameter(name: 'poll', type: 'boolean'), ], doc: 'Subversion') - method(name: 'unstash', type: 'Object', params: [name:'java.lang.String'], doc: 'Restore files previously stashed') - method(name: 'withCredentials', type: 'Object', params: [bindings:Map, body:'Closure'], doc: 'Bind credentials to variables') - method(name: 'wrap', type: 'Object', params: [delegate:Map, body:'Closure'], doc: 'General Build Wrapper') - method(name: 'writeFile', type: 'Object', namedParams: [parameter(name: 'file', type: 'java.lang.String'), parameter(name: 'text', type: 'java.lang.String'), parameter(name: 'encoding', type: 'java.lang.String'), ], doc: 'Write file to workspace') - method(name: 'archive', type: 'Object', params: [includes:'java.lang.String'], doc: 'Advanced/Deprecated Archive artifacts') - method(name: 'archive', type: 'Object', namedParams: [parameter(name: 'includes', type: 'java.lang.String'), parameter(name: 'excludes', type: 'java.lang.String'), ], doc: 'Archive artifacts') - method(name: 'dockerFingerprintFrom', type: 'Object', namedParams: [parameter(name: 'dockerfile', type: 'java.lang.String'), parameter(name: 'image', type: 'java.lang.String'), parameter(name: 'toolName', type: 'java.lang.String'), ], doc: 'Record trace of a Docker image used in FROM') - method(name: 'unarchive', type: 'Object', params: [:], doc: 'Advanced/Deprecated Copy archived artifacts into the workspace') - method(name: 'unarchive', type: 'Object', namedParams: [parameter(name: 'mapping', type: 'Map'), ], doc: 'Copy archived artifacts into the workspace') - method(name: 'withDockerContainer', type: 'Object', params: [image:java.lang.String, body:'Closure'], doc: 'Advanced/Deprecated Run build steps inside a Docker container') - method(name: 'withDockerContainer', type: 'Object', params: [body:Closure], namedParams: [parameter(name: 'image', type: 'java.lang.String'), parameter(name: 'args', type: 'java.lang.String'), parameter(name: 'toolName', type: 'java.lang.String'), ], doc: 'Run build steps inside a Docker container') - method(name: 'withDockerRegistry', type: 'Object', params: [registry:Map, body:'Closure'], doc: 'Advanced/Deprecated Sets up Docker registry endpoint') - method(name: 'withDockerServer', type: 'Object', params: [server:Map, body:'Closure'], doc: 'Advanced/Deprecated Sets up Docker server endpoint') - } -} diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml deleted file mode 100644 index e82256df..00000000 --- a/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - diff --git a/src/main/resources/swagger/api.yml b/src/main/resources/swagger/api.yml deleted file mode 100644 index fc271ea0..00000000 --- a/src/main/resources/swagger/api.yml +++ /dev/null @@ -1,7 +0,0 @@ -# API-first development with OpenAPI -# This file will be used at compile time to generate Spring-MVC endpoint stubs using openapi-generator -openapi: "3.0.1" -info: - title: "hsadminNg" - version: 0.0.1 -paths: {} \ No newline at end of file diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html deleted file mode 100644 index 08616bcf..00000000 --- a/src/main/resources/templates/error.html +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - Your request cannot be processed - - - -

-

Your request cannot be processed :(

- -

Sorry, an error has occurred.

- - Status:  ()
- - Message: 
-
- - - -
- - diff --git a/src/main/resources/templates/mail/activationEmail.html b/src/main/resources/templates/mail/activationEmail.html deleted file mode 100644 index cb021d8e..00000000 --- a/src/main/resources/templates/mail/activationEmail.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - JHipster activation - - - - -

- Dear -

-

- Your JHipster account has been created, please click on the URL below to activate it: -

-

- Activation link -

-

- Regards, -
- JHipster. -

- - diff --git a/src/main/resources/templates/mail/creationEmail.html b/src/main/resources/templates/mail/creationEmail.html deleted file mode 100644 index dc0cff58..00000000 --- a/src/main/resources/templates/mail/creationEmail.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - JHipster creation - - - - -

- Dear -

-

- Your JHipster account has been created, please click on the URL below to access it: -

-

- Login link -

-

- Regards, -
- JHipster. -

- - diff --git a/src/main/resources/templates/mail/passwordResetEmail.html b/src/main/resources/templates/mail/passwordResetEmail.html deleted file mode 100644 index f4451126..00000000 --- a/src/main/resources/templates/mail/passwordResetEmail.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - JHipster password reset - - - - -

- Dear -

-

- For your JHipster account a password reset was requested, please click on the URL below to reset it: -

-

- Login link -

-

- Regards, -
- JHipster. -

- - diff --git a/src/main/webapp/404.html b/src/main/webapp/404.html deleted file mode 100644 index 086998e1..00000000 --- a/src/main/webapp/404.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - Page Not Found - - - - - -

Page Not Found

-

Sorry, but the page you were trying to view does not exist.

- - - diff --git a/src/main/webapp/app/account/account.module.ts b/src/main/webapp/app/account/account.module.ts deleted file mode 100644 index 9731312c..00000000 --- a/src/main/webapp/app/account/account.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { HsadminNgSharedModule } from 'app/shared'; - -import { - PasswordStrengthBarComponent, - RegisterComponent, - ActivateComponent, - PasswordComponent, - PasswordResetInitComponent, - PasswordResetFinishComponent, - SettingsComponent, - accountState -} from './'; - -@NgModule({ - imports: [HsadminNgSharedModule, RouterModule.forChild(accountState)], - declarations: [ - ActivateComponent, - RegisterComponent, - PasswordComponent, - PasswordStrengthBarComponent, - PasswordResetInitComponent, - PasswordResetFinishComponent, - SettingsComponent - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgAccountModule {} diff --git a/src/main/webapp/app/account/account.route.ts b/src/main/webapp/app/account/account.route.ts deleted file mode 100644 index f849342e..00000000 --- a/src/main/webapp/app/account/account.route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Routes } from '@angular/router'; - -import { activateRoute, passwordRoute, passwordResetFinishRoute, passwordResetInitRoute, registerRoute, settingsRoute } from './'; - -const ACCOUNT_ROUTES = [activateRoute, passwordRoute, passwordResetFinishRoute, passwordResetInitRoute, registerRoute, settingsRoute]; - -export const accountState: Routes = [ - { - path: '', - children: ACCOUNT_ROUTES - } -]; diff --git a/src/main/webapp/app/account/activate/activate.component.html b/src/main/webapp/app/account/activate/activate.component.html deleted file mode 100644 index 6a28eef7..00000000 --- a/src/main/webapp/app/account/activate/activate.component.html +++ /dev/null @@ -1,17 +0,0 @@ -
-
-
-

Activation

- -
- Your user account has been activated. Please - sign in. -
- -
- Your user could not be activated. Please use the registration form to sign up. -
- -
-
-
diff --git a/src/main/webapp/app/account/activate/activate.component.ts b/src/main/webapp/app/account/activate/activate.component.ts deleted file mode 100644 index 5c398073..00000000 --- a/src/main/webapp/app/account/activate/activate.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { ActivatedRoute } from '@angular/router'; - -import { LoginModalService } from 'app/core'; -import { ActivateService } from './activate.service'; - -@Component({ - selector: 'jhi-activate', - templateUrl: './activate.component.html' -}) -export class ActivateComponent implements OnInit { - error: string; - success: string; - modalRef: NgbModalRef; - - constructor(private activateService: ActivateService, private loginModalService: LoginModalService, private route: ActivatedRoute) {} - - ngOnInit() { - this.route.queryParams.subscribe(params => { - this.activateService.get(params['key']).subscribe( - () => { - this.error = null; - this.success = 'OK'; - }, - () => { - this.success = null; - this.error = 'ERROR'; - } - ); - }); - } - - login() { - this.modalRef = this.loginModalService.open(); - } -} diff --git a/src/main/webapp/app/account/activate/activate.route.ts b/src/main/webapp/app/account/activate/activate.route.ts deleted file mode 100644 index 524e332d..00000000 --- a/src/main/webapp/app/account/activate/activate.route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Route } from '@angular/router'; - -import { ActivateComponent } from './activate.component'; - -export const activateRoute: Route = { - path: 'activate', - component: ActivateComponent, - data: { - authorities: [], - pageTitle: 'activate.title' - } -}; diff --git a/src/main/webapp/app/account/activate/activate.service.ts b/src/main/webapp/app/account/activate/activate.service.ts deleted file mode 100644 index adade9ef..00000000 --- a/src/main/webapp/app/account/activate/activate.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpParams } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable({ providedIn: 'root' }) -export class ActivateService { - constructor(private http: HttpClient) {} - - get(key: string): Observable { - return this.http.get(SERVER_API_URL + 'api/activate', { - params: new HttpParams().set('key', key) - }); - } -} diff --git a/src/main/webapp/app/account/index.ts b/src/main/webapp/app/account/index.ts deleted file mode 100644 index aeada055..00000000 --- a/src/main/webapp/app/account/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -export * from './activate/activate.component'; -export * from './activate/activate.service'; -export * from './activate/activate.route'; -export * from './password/password.component'; -export * from './password/password-strength-bar.component'; -export * from './password/password.service'; -export * from './password/password.route'; -export * from './password-reset/finish/password-reset-finish.component'; -export * from './password-reset/finish/password-reset-finish.service'; -export * from './password-reset/finish/password-reset-finish.route'; -export * from './password-reset/init/password-reset-init.component'; -export * from './password-reset/init/password-reset-init.service'; -export * from './password-reset/init/password-reset-init.route'; -export * from './register/register.component'; -export * from './register/register.service'; -export * from './register/register.route'; -export * from './settings/settings.component'; -export * from './settings/settings.route'; -export * from './account.route'; diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html deleted file mode 100644 index bcbc9111..00000000 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html +++ /dev/null @@ -1,77 +0,0 @@ -
-
-
-

Reset password

- -
- The password reset key is missing. -
- -
-

Choose a new password

-
- -
-

Your password couldn't be reset. Remember a password request is only valid for 24 hours.

-
- -

- Your password has been reset. Please - sign in. -

- -
- The password and its confirmation do not match! -
- -
-
-
- - -
- - Your password is required. - - - Your password is required to be at least 4 characters. - - - Your password cannot be longer than 50 characters. - -
- -
- -
- - -
- - Your password confirmation is required. - - - Your password confirmation is required to be at least 4 characters. - - - Your password confirmation cannot be longer than 50 characters. - -
-
- -
-
- -
-
-
diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts deleted file mode 100644 index 72aac25c..00000000 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; -import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { ActivatedRoute } from '@angular/router'; - -import { LoginModalService } from 'app/core'; -import { PasswordResetFinishService } from './password-reset-finish.service'; - -@Component({ - selector: 'jhi-password-reset-finish', - templateUrl: './password-reset-finish.component.html' -}) -export class PasswordResetFinishComponent implements OnInit, AfterViewInit { - confirmPassword: string; - doNotMatch: string; - error: string; - keyMissing: boolean; - resetAccount: any; - success: string; - modalRef: NgbModalRef; - key: string; - - constructor( - private passwordResetFinishService: PasswordResetFinishService, - private loginModalService: LoginModalService, - private route: ActivatedRoute, - private elementRef: ElementRef, - private renderer: Renderer - ) {} - - ngOnInit() { - this.route.queryParams.subscribe(params => { - this.key = params['key']; - }); - this.resetAccount = {}; - this.keyMissing = !this.key; - } - - ngAfterViewInit() { - if (this.elementRef.nativeElement.querySelector('#password') != null) { - this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#password'), 'focus', []); - } - } - - finishReset() { - this.doNotMatch = null; - this.error = null; - if (this.resetAccount.password !== this.confirmPassword) { - this.doNotMatch = 'ERROR'; - } else { - this.passwordResetFinishService.save({ key: this.key, newPassword: this.resetAccount.password }).subscribe( - () => { - this.success = 'OK'; - }, - () => { - this.success = null; - this.error = 'ERROR'; - } - ); - } - } - - login() { - this.modalRef = this.loginModalService.open(); - } -} diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts deleted file mode 100644 index 686cb972..00000000 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Route } from '@angular/router'; - -import { PasswordResetFinishComponent } from './password-reset-finish.component'; - -export const passwordResetFinishRoute: Route = { - path: 'reset/finish', - component: PasswordResetFinishComponent, - data: { - authorities: [], - pageTitle: 'global.menu.account.password' - } -}; diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts deleted file mode 100644 index 706bdaa5..00000000 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable({ providedIn: 'root' }) -export class PasswordResetFinishService { - constructor(private http: HttpClient) {} - - save(keyAndPassword: any): Observable { - return this.http.post(SERVER_API_URL + 'api/account/reset-password/finish', keyAndPassword); - } -} diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html deleted file mode 100644 index 8f42e600..00000000 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html +++ /dev/null @@ -1,46 +0,0 @@ -
-
-
-

Reset your password

- -
- Email address isn't registered! Please check and try again. -
- -
-

Enter the email address you used to register.

-
- -
-

Check your emails for details on how to reset your password.

-
- -
-
- - -
- - Your email is required. - - - Your email is invalid. - - - Your email is required to be at least 5 characters. - - - Your email cannot be longer than 100 characters. - -
-
- -
-
-
-
diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts deleted file mode 100644 index e3261734..00000000 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; -import { EMAIL_NOT_FOUND_TYPE } from 'app/shared'; -import { PasswordResetInitService } from './password-reset-init.service'; - -@Component({ - selector: 'jhi-password-reset-init', - templateUrl: './password-reset-init.component.html' -}) -export class PasswordResetInitComponent implements OnInit, AfterViewInit { - error: string; - errorEmailNotExists: string; - resetAccount: any; - success: string; - - constructor(private passwordResetInitService: PasswordResetInitService, private elementRef: ElementRef, private renderer: Renderer) {} - - ngOnInit() { - this.resetAccount = {}; - } - - ngAfterViewInit() { - this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#email'), 'focus', []); - } - - requestReset() { - this.error = null; - this.errorEmailNotExists = null; - - this.passwordResetInitService.save(this.resetAccount.email).subscribe( - () => { - this.success = 'OK'; - }, - response => { - this.success = null; - if (response.status === 400 && response.error.type === EMAIL_NOT_FOUND_TYPE) { - this.errorEmailNotExists = 'ERROR'; - } else { - this.error = 'ERROR'; - } - } - ); - } -} diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts deleted file mode 100644 index 6d7da08c..00000000 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Route } from '@angular/router'; - -import { PasswordResetInitComponent } from './password-reset-init.component'; - -export const passwordResetInitRoute: Route = { - path: 'reset/request', - component: PasswordResetInitComponent, - data: { - authorities: [], - pageTitle: 'global.menu.account.password' - } -}; diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts deleted file mode 100644 index c24ccf94..00000000 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable({ providedIn: 'root' }) -export class PasswordResetInitService { - constructor(private http: HttpClient) {} - - save(mail: string): Observable { - return this.http.post(SERVER_API_URL + 'api/account/reset-password/init', mail); - } -} diff --git a/src/main/webapp/app/account/password/password-strength-bar.component.ts b/src/main/webapp/app/account/password/password-strength-bar.component.ts deleted file mode 100644 index 19f33019..00000000 --- a/src/main/webapp/app/account/password/password-strength-bar.component.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Component, ElementRef, Input, Renderer } from '@angular/core'; - -@Component({ - selector: 'jhi-password-strength-bar', - template: ` -
- Password strength: -
    -
  • -
  • -
  • -
  • -
  • -
-
- `, - styleUrls: ['password-strength-bar.css'] -}) -export class PasswordStrengthBarComponent { - colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0']; - - constructor(private renderer: Renderer, private elementRef: ElementRef) {} - - measureStrength(p: string): number { - let force = 0; - const regex = /[$-/:-?{-~!"^_`\[\]]/g; // " - const lowerLetters = /[a-z]+/.test(p); - const upperLetters = /[A-Z]+/.test(p); - const numbers = /[0-9]+/.test(p); - const symbols = regex.test(p); - - const flags = [lowerLetters, upperLetters, numbers, symbols]; - const passedMatches = flags.filter((isMatchedFlag: boolean) => { - return isMatchedFlag === true; - }).length; - - force += 2 * p.length + (p.length >= 10 ? 1 : 0); - force += passedMatches * 10; - - // penalty (short password) - force = p.length <= 6 ? Math.min(force, 10) : force; - - // penalty (poor variety of characters) - force = passedMatches === 1 ? Math.min(force, 10) : force; - force = passedMatches === 2 ? Math.min(force, 20) : force; - force = passedMatches === 3 ? Math.min(force, 40) : force; - - return force; - } - - getColor(s: number): any { - let idx = 0; - if (s <= 10) { - idx = 0; - } else if (s <= 20) { - idx = 1; - } else if (s <= 30) { - idx = 2; - } else if (s <= 40) { - idx = 3; - } else { - idx = 4; - } - return { idx: idx + 1, col: this.colors[idx] }; - } - - @Input() - set passwordToCheck(password: string) { - if (password) { - const c = this.getColor(this.measureStrength(password)); - const element = this.elementRef.nativeElement; - if (element.className) { - this.renderer.setElementClass(element, element.className, false); - } - const lis = element.getElementsByTagName('li'); - for (let i = 0; i < lis.length; i++) { - if (i < c.idx) { - this.renderer.setElementStyle(lis[i], 'backgroundColor', c.col); - } else { - this.renderer.setElementStyle(lis[i], 'backgroundColor', '#DDD'); - } - } - } - } -} diff --git a/src/main/webapp/app/account/password/password-strength-bar.css b/src/main/webapp/app/account/password/password-strength-bar.css deleted file mode 100644 index b6ad1fa6..00000000 --- a/src/main/webapp/app/account/password/password-strength-bar.css +++ /dev/null @@ -1,24 +0,0 @@ -/* ========================================================================== -start Password strength bar style -========================================================================== */ -ul#strengthBar { - display: inline; - list-style: none; - margin: 0; - margin-left: 15px; - padding: 0; - vertical-align: 2px; -} - -.point { - background: #ddd; - border-radius: 2px; - display: inline-block; - height: 5px; - margin-right: 1px; - width: 20px; -} - -.point:last-child { - margin: 0 !important; -} diff --git a/src/main/webapp/app/account/password/password.component.html b/src/main/webapp/app/account/password/password.component.html deleted file mode 100644 index 6120b905..00000000 --- a/src/main/webapp/app/account/password/password.component.html +++ /dev/null @@ -1,77 +0,0 @@ -
-
-
-

Password for [{{account.login}}]

- -
- Password changed! -
-
- An error has occurred! The password could not be changed. -
- -
- The password and its confirmation do not match! -
- -
- -
- - -
- - Your password is required. - -
-
-
- - -
- - Your password is required. - - - Your password is required to be at least 4 characters. - - - Your password cannot be longer than 50 characters. - -
- -
-
- - -
- - Your confirmation password is required. - - - Your confirmation password is required to be at least 4 characters. - - - Your confirmation password cannot be longer than 50 characters. - -
-
- - -
-
-
-
diff --git a/src/main/webapp/app/account/password/password.component.ts b/src/main/webapp/app/account/password/password.component.ts deleted file mode 100644 index 3004effa..00000000 --- a/src/main/webapp/app/account/password/password.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { AccountService } from 'app/core'; -import { PasswordService } from './password.service'; - -@Component({ - selector: 'jhi-password', - templateUrl: './password.component.html' -}) -export class PasswordComponent implements OnInit { - doNotMatch: string; - error: string; - success: string; - account: any; - currentPassword: string; - newPassword: string; - confirmPassword: string; - - constructor(private passwordService: PasswordService, private accountService: AccountService) {} - - ngOnInit() { - this.accountService.identity().then(account => { - this.account = account; - }); - } - - changePassword() { - if (this.newPassword !== this.confirmPassword) { - this.error = null; - this.success = null; - this.doNotMatch = 'ERROR'; - } else { - this.doNotMatch = null; - this.passwordService.save(this.newPassword, this.currentPassword).subscribe( - () => { - this.error = null; - this.success = 'OK'; - }, - () => { - this.success = null; - this.error = 'ERROR'; - } - ); - } - } -} diff --git a/src/main/webapp/app/account/password/password.route.ts b/src/main/webapp/app/account/password/password.route.ts deleted file mode 100644 index 2a6ec475..00000000 --- a/src/main/webapp/app/account/password/password.route.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Route } from '@angular/router'; - -import { UserRouteAccessService } from 'app/core'; -import { PasswordComponent } from './password.component'; - -export const passwordRoute: Route = { - path: 'password', - component: PasswordComponent, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'global.menu.account.password' - }, - canActivate: [UserRouteAccessService] -}; diff --git a/src/main/webapp/app/account/password/password.service.ts b/src/main/webapp/app/account/password/password.service.ts deleted file mode 100644 index 028df7b0..00000000 --- a/src/main/webapp/app/account/password/password.service.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable({ providedIn: 'root' }) -export class PasswordService { - constructor(private http: HttpClient) {} - - save(newPassword: string, currentPassword: string): Observable { - return this.http.post(SERVER_API_URL + 'api/account/change-password', { currentPassword, newPassword }); - } -} diff --git a/src/main/webapp/app/account/register/register.component.html b/src/main/webapp/app/account/register/register.component.html deleted file mode 100644 index fd5c89e2..00000000 --- a/src/main/webapp/app/account/register/register.component.html +++ /dev/null @@ -1,124 +0,0 @@ -
-
-
-

Registration

- -
- Registration saved! Please check your email for confirmation. -
- -
- Registration failed! Please try again later. -
- -
- Login name already registered! Please choose another one. -
- -
- Email is already in use! Please choose another one. -
- -
- The password and its confirmation do not match! -
-
-
-
-
-
-
- - -
- - Your username is required. - - - Your username is required to be at least 1 character. - - - Your username cannot be longer than 50 characters. - - - Your username can only contain letters and digits. - -
-
-
- - -
- - Your email is required. - - - Your email is invalid. - - - Your email is required to be at least 5 characters. - - - Your email cannot be longer than 100 characters. - -
-
-
- - -
- - Your password is required. - - - Your password is required to be at least 4 characters. - - - Your password cannot be longer than 50 characters. - -
- -
-
- - -
- - Your confirmation password is required. - - - Your confirmation password is required to be at least 4 characters. - - - Your confirmation password cannot be longer than 50 characters. - -
-
- - -
-

-
- If you want to - sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and password="user").
-
-
-
-
diff --git a/src/main/webapp/app/account/register/register.component.ts b/src/main/webapp/app/account/register/register.component.ts deleted file mode 100644 index de97a880..00000000 --- a/src/main/webapp/app/account/register/register.component.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; -import { HttpErrorResponse } from '@angular/common/http'; -import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { JhiLanguageService } from 'ng-jhipster'; - -import { EMAIL_ALREADY_USED_TYPE, LOGIN_ALREADY_USED_TYPE } from 'app/shared'; -import { LoginModalService } from 'app/core'; -import { Register } from './register.service'; - -@Component({ - selector: 'jhi-register', - templateUrl: './register.component.html' -}) -export class RegisterComponent implements OnInit, AfterViewInit { - confirmPassword: string; - doNotMatch: string; - error: string; - errorEmailExists: string; - errorUserExists: string; - registerAccount: any; - success: boolean; - modalRef: NgbModalRef; - - constructor( - private languageService: JhiLanguageService, - private loginModalService: LoginModalService, - private registerService: Register, - private elementRef: ElementRef, - private renderer: Renderer - ) {} - - ngOnInit() { - this.success = false; - this.registerAccount = {}; - } - - ngAfterViewInit() { - this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#login'), 'focus', []); - } - - register() { - if (this.registerAccount.password !== this.confirmPassword) { - this.doNotMatch = 'ERROR'; - } else { - this.doNotMatch = null; - this.error = null; - this.errorUserExists = null; - this.errorEmailExists = null; - this.languageService.getCurrent().then(key => { - this.registerAccount.langKey = key; - this.registerService.save(this.registerAccount).subscribe( - () => { - this.success = true; - }, - response => this.processError(response) - ); - }); - } - } - - openLogin() { - this.modalRef = this.loginModalService.open(); - } - - private processError(response: HttpErrorResponse) { - this.success = null; - if (response.status === 400 && response.error.type === LOGIN_ALREADY_USED_TYPE) { - this.errorUserExists = 'ERROR'; - } else if (response.status === 400 && response.error.type === EMAIL_ALREADY_USED_TYPE) { - this.errorEmailExists = 'ERROR'; - } else { - this.error = 'ERROR'; - } - } -} diff --git a/src/main/webapp/app/account/register/register.route.ts b/src/main/webapp/app/account/register/register.route.ts deleted file mode 100644 index ca834d5d..00000000 --- a/src/main/webapp/app/account/register/register.route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Route } from '@angular/router'; - -import { RegisterComponent } from './register.component'; - -export const registerRoute: Route = { - path: 'register', - component: RegisterComponent, - data: { - authorities: [], - pageTitle: 'register.title' - } -}; diff --git a/src/main/webapp/app/account/register/register.service.ts b/src/main/webapp/app/account/register/register.service.ts deleted file mode 100644 index dfe6f1da..00000000 --- a/src/main/webapp/app/account/register/register.service.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable({ providedIn: 'root' }) -export class Register { - constructor(private http: HttpClient) {} - - save(account: any): Observable { - return this.http.post(SERVER_API_URL + 'api/register', account); - } -} diff --git a/src/main/webapp/app/account/settings/settings.component.html b/src/main/webapp/app/account/settings/settings.component.html deleted file mode 100644 index e79860b5..00000000 --- a/src/main/webapp/app/account/settings/settings.component.html +++ /dev/null @@ -1,86 +0,0 @@ -
-
-
-

User settings for [{{settingsAccount.login}}]

- -
- Settings saved! -
- - - -
- -
- - -
- - Your first name is required. - - - Your first name is required to be at least 1 character. - - - Your first name cannot be longer than 50 characters. - -
-
-
- - -
- - Your last name is required. - - - Your last name is required to be at least 1 character. - - - Your last name cannot be longer than 50 characters. - -
-
-
- - -
- - Your email is required. - - - Your email is invalid. - - - Your email is required to be at least 5 characters. - - - Your email cannot be longer than 100 characters. - -
-
-
- - -
- -
-
-
- -
diff --git a/src/main/webapp/app/account/settings/settings.component.ts b/src/main/webapp/app/account/settings/settings.component.ts deleted file mode 100644 index 3d5af649..00000000 --- a/src/main/webapp/app/account/settings/settings.component.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { JhiLanguageService } from 'ng-jhipster'; - -import { AccountService, JhiLanguageHelper } from 'app/core'; - -@Component({ - selector: 'jhi-settings', - templateUrl: './settings.component.html' -}) -export class SettingsComponent implements OnInit { - error: string; - success: string; - settingsAccount: any; - languages: any[]; - - constructor( - private accountService: AccountService, - private languageService: JhiLanguageService, - private languageHelper: JhiLanguageHelper - ) {} - - ngOnInit() { - this.accountService.identity().then(account => { - this.settingsAccount = this.copyAccount(account); - }); - this.languageHelper.getAll().then(languages => { - this.languages = languages; - }); - } - - save() { - this.accountService.save(this.settingsAccount).subscribe( - () => { - this.error = null; - this.success = 'OK'; - this.accountService.identity(true).then(account => { - this.settingsAccount = this.copyAccount(account); - }); - this.languageService.getCurrent().then(current => { - if (this.settingsAccount.langKey !== current) { - this.languageService.changeLanguage(this.settingsAccount.langKey); - } - }); - }, - () => { - this.success = null; - this.error = 'ERROR'; - } - ); - } - - copyAccount(account) { - return { - activated: account.activated, - email: account.email, - firstName: account.firstName, - langKey: account.langKey, - lastName: account.lastName, - login: account.login, - imageUrl: account.imageUrl - }; - } -} diff --git a/src/main/webapp/app/account/settings/settings.route.ts b/src/main/webapp/app/account/settings/settings.route.ts deleted file mode 100644 index 95c2de6e..00000000 --- a/src/main/webapp/app/account/settings/settings.route.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Route } from '@angular/router'; - -import { UserRouteAccessService } from 'app/core'; -import { SettingsComponent } from './settings.component'; - -export const settingsRoute: Route = { - path: 'settings', - component: SettingsComponent, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'global.menu.account.settings' - }, - canActivate: [UserRouteAccessService] -}; diff --git a/src/main/webapp/app/admin/admin.module.ts b/src/main/webapp/app/admin/admin.module.ts deleted file mode 100644 index c430a8f5..00000000 --- a/src/main/webapp/app/admin/admin.module.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { JhiLanguageService } from 'ng-jhipster'; -import { JhiLanguageHelper } from 'app/core'; -import { HsadminNgSharedModule } from 'app/shared'; -/* jhipster-needle-add-admin-module-import - JHipster will add admin modules imports here */ - -import { - adminState, - AuditsComponent, - UserMgmtComponent, - UserMgmtDetailComponent, - UserMgmtUpdateComponent, - UserMgmtDeleteDialogComponent, - LogsComponent, - JhiMetricsMonitoringComponent, - JhiHealthModalComponent, - JhiHealthCheckComponent, - JhiConfigurationComponent, - JhiDocsComponent -} from './'; - -@NgModule({ - imports: [ - HsadminNgSharedModule, - RouterModule.forChild(adminState) - /* jhipster-needle-add-admin-module - JHipster will add admin modules here */ - ], - declarations: [ - AuditsComponent, - UserMgmtComponent, - UserMgmtDetailComponent, - UserMgmtUpdateComponent, - UserMgmtDeleteDialogComponent, - LogsComponent, - JhiConfigurationComponent, - JhiHealthCheckComponent, - JhiHealthModalComponent, - JhiDocsComponent, - JhiMetricsMonitoringComponent - ], - providers: [{ provide: JhiLanguageService, useClass: JhiLanguageService }], - entryComponents: [UserMgmtDeleteDialogComponent, JhiHealthModalComponent], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgAdminModule { - constructor(private languageService: JhiLanguageService, private languageHelper: JhiLanguageHelper) { - this.languageHelper.language.subscribe((languageKey: string) => { - if (languageKey !== undefined) { - this.languageService.changeLanguage(languageKey); - } - }); - } -} diff --git a/src/main/webapp/app/admin/admin.route.ts b/src/main/webapp/app/admin/admin.route.ts deleted file mode 100644 index 88c7e575..00000000 --- a/src/main/webapp/app/admin/admin.route.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Routes } from '@angular/router'; - -import { auditsRoute, configurationRoute, docsRoute, healthRoute, logsRoute, metricsRoute, userMgmtRoute } from './'; - -import { UserRouteAccessService } from 'app/core'; - -const ADMIN_ROUTES = [auditsRoute, configurationRoute, docsRoute, healthRoute, logsRoute, ...userMgmtRoute, metricsRoute]; - -export const adminState: Routes = [ - { - path: '', - data: { - authorities: ['ROLE_ADMIN'] - }, - canActivate: [UserRouteAccessService], - children: ADMIN_ROUTES - } -]; diff --git a/src/main/webapp/app/admin/audits/audit-data.model.ts b/src/main/webapp/app/admin/audits/audit-data.model.ts deleted file mode 100644 index a2506c40..00000000 --- a/src/main/webapp/app/admin/audits/audit-data.model.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class AuditData { - constructor(public remoteAddress: string, public sessionId: string) {} -} diff --git a/src/main/webapp/app/admin/audits/audit.model.ts b/src/main/webapp/app/admin/audits/audit.model.ts deleted file mode 100644 index 6497fb44..00000000 --- a/src/main/webapp/app/admin/audits/audit.model.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { AuditData } from './audit-data.model'; - -export class Audit { - constructor(public data: AuditData, public principal: string, public timestamp: string, public type: string) {} -} diff --git a/src/main/webapp/app/admin/audits/audits.component.html b/src/main/webapp/app/admin/audits/audits.component.html deleted file mode 100644 index e55799e9..00000000 --- a/src/main/webapp/app/admin/audits/audits.component.html +++ /dev/null @@ -1,52 +0,0 @@ -
-

Audits

- -
-
-

Filter by date

-
-
- from -
- - -
- To -
- -
-
-
- -
- - - - - - - - - - - - - - - - - -
DateUserStateExtra data
{{audit.timestamp| date:'medium'}}{{audit.principal}}{{audit.type}} - {{audit.data.message}} - Remote Address {{audit.data.remoteAddress}} -
-
-
-
- -
-
- -
-
-
diff --git a/src/main/webapp/app/admin/audits/audits.component.ts b/src/main/webapp/app/admin/audits/audits.component.ts deleted file mode 100644 index 21739275..00000000 --- a/src/main/webapp/app/admin/audits/audits.component.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { DatePipe } from '@angular/common'; -import { ActivatedRoute, Router } from '@angular/router'; -import { JhiParseLinks, JhiAlertService } from 'ng-jhipster'; - -import { ITEMS_PER_PAGE } from 'app/shared'; -import { Audit } from './audit.model'; -import { AuditsService } from './audits.service'; - -@Component({ - selector: 'jhi-audit', - templateUrl: './audits.component.html' -}) -export class AuditsComponent implements OnInit, OnDestroy { - audits: Audit[]; - fromDate: string; - itemsPerPage: any; - links: any; - page: number; - routeData: any; - predicate: any; - previousPage: any; - reverse: boolean; - toDate: string; - totalItems: number; - - constructor( - private auditsService: AuditsService, - private alertService: JhiAlertService, - private parseLinks: JhiParseLinks, - private activatedRoute: ActivatedRoute, - private datePipe: DatePipe, - private router: Router - ) { - this.itemsPerPage = ITEMS_PER_PAGE; - this.routeData = this.activatedRoute.data.subscribe(data => { - this.page = data['pagingParams'].page; - this.previousPage = data['pagingParams'].page; - this.reverse = data['pagingParams'].ascending; - this.predicate = data['pagingParams'].predicate; - }); - } - - ngOnInit() { - this.today(); - this.previousMonth(); - this.loadAll(); - } - - ngOnDestroy() { - this.routeData.unsubscribe(); - } - - previousMonth() { - const dateFormat = 'yyyy-MM-dd'; - let fromDate: Date = new Date(); - - if (fromDate.getMonth() === 0) { - fromDate = new Date(fromDate.getFullYear() - 1, 11, fromDate.getDate()); - } else { - fromDate = new Date(fromDate.getFullYear(), fromDate.getMonth() - 1, fromDate.getDate()); - } - - this.fromDate = this.datePipe.transform(fromDate, dateFormat); - } - - today() { - const dateFormat = 'yyyy-MM-dd'; - // Today + 1 day - needed if the current day must be included - const today: Date = new Date(); - today.setDate(today.getDate() + 1); - const date = new Date(today.getFullYear(), today.getMonth(), today.getDate()); - this.toDate = this.datePipe.transform(date, dateFormat); - } - - loadAll() { - this.auditsService - .query({ - page: this.page - 1, - size: this.itemsPerPage, - sort: this.sort(), - fromDate: this.fromDate, - toDate: this.toDate - }) - .subscribe( - (res: HttpResponse) => this.onSuccess(res.body, res.headers), - (res: HttpResponse) => this.onError(res.body) - ); - } - - sort() { - const result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; - if (this.predicate !== 'id') { - result.push('id'); - } - return result; - } - - loadPage(page: number) { - if (page !== this.previousPage) { - this.previousPage = page; - this.transition(); - } - } - - transition() { - this.router.navigate(['/admin/audits'], { - queryParams: { - page: this.page, - sort: this.predicate + ',' + (this.reverse ? 'asc' : 'desc') - } - }); - this.loadAll(); - } - - private onSuccess(data, headers) { - this.links = this.parseLinks.parse(headers.get('link')); - this.totalItems = headers.get('X-Total-Count'); - this.audits = data; - } - - private onError(error) { - this.alertService.error(error.error, error.message, null); - } -} diff --git a/src/main/webapp/app/admin/audits/audits.route.ts b/src/main/webapp/app/admin/audits/audits.route.ts deleted file mode 100644 index fa049531..00000000 --- a/src/main/webapp/app/admin/audits/audits.route.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Route } from '@angular/router'; -import { JhiPaginationUtil, JhiResolvePagingParams } from 'ng-jhipster'; - -import { AuditsComponent } from './audits.component'; - -export const auditsRoute: Route = { - path: 'audits', - component: AuditsComponent, - resolve: { - pagingParams: JhiResolvePagingParams - }, - data: { - pageTitle: 'audits.title', - defaultSort: 'auditEventDate,desc' - } -}; diff --git a/src/main/webapp/app/admin/audits/audits.service.ts b/src/main/webapp/app/admin/audits/audits.service.ts deleted file mode 100644 index 78e8cca7..00000000 --- a/src/main/webapp/app/admin/audits/audits.service.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { createRequestOption } from 'app/shared'; -import { SERVER_API_URL } from 'app/app.constants'; -import { Audit } from './audit.model'; - -@Injectable({ providedIn: 'root' }) -export class AuditsService { - constructor(private http: HttpClient) {} - - query(req: any): Observable> { - const params: HttpParams = createRequestOption(req); - params.set('fromDate', req.fromDate); - params.set('toDate', req.toDate); - - const requestURL = SERVER_API_URL + 'management/audits'; - - return this.http.get(requestURL, { - params, - observe: 'response' - }); - } -} diff --git a/src/main/webapp/app/admin/configuration/configuration.component.html b/src/main/webapp/app/admin/configuration/configuration.component.html deleted file mode 100644 index c95775a0..00000000 --- a/src/main/webapp/app/admin/configuration/configuration.component.html +++ /dev/null @@ -1,46 +0,0 @@ -
-

Configuration

- - Filter (by prefix) -

Spring configuration

- - - - - - - - - - - - - -
PrefixProperties
{{entry.prefix}} -
-
{{key}}
-
- {{entry.properties[key] | json}} -
-
-
-
-

{{key}}

- - - - - - - - - - - - - -
PropertyValue
{{item.key}} - {{item.val}} -
-
-
diff --git a/src/main/webapp/app/admin/configuration/configuration.component.ts b/src/main/webapp/app/admin/configuration/configuration.component.ts deleted file mode 100644 index 6867210c..00000000 --- a/src/main/webapp/app/admin/configuration/configuration.component.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { JhiConfigurationService } from './configuration.service'; - -@Component({ - selector: 'jhi-configuration', - templateUrl: './configuration.component.html' -}) -export class JhiConfigurationComponent implements OnInit { - allConfiguration: any = null; - configuration: any = null; - configKeys: any[]; - filter: string; - orderProp: string; - reverse: boolean; - - constructor(private configurationService: JhiConfigurationService) { - this.configKeys = []; - this.filter = ''; - this.orderProp = 'prefix'; - this.reverse = false; - } - - keys(dict): Array { - return dict === undefined ? [] : Object.keys(dict); - } - - ngOnInit() { - this.configurationService.get().subscribe(configuration => { - this.configuration = configuration; - - for (const config of configuration) { - if (config.properties !== undefined) { - this.configKeys.push(Object.keys(config.properties)); - } - } - }); - - this.configurationService.getEnv().subscribe(configuration => { - this.allConfiguration = configuration; - }); - } -} diff --git a/src/main/webapp/app/admin/configuration/configuration.route.ts b/src/main/webapp/app/admin/configuration/configuration.route.ts deleted file mode 100644 index 1ff61dfa..00000000 --- a/src/main/webapp/app/admin/configuration/configuration.route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Route } from '@angular/router'; - -import { JhiConfigurationComponent } from './configuration.component'; - -export const configurationRoute: Route = { - path: 'jhi-configuration', - component: JhiConfigurationComponent, - data: { - pageTitle: 'configuration.title' - } -}; diff --git a/src/main/webapp/app/admin/configuration/configuration.service.ts b/src/main/webapp/app/admin/configuration/configuration.service.ts deleted file mode 100644 index d59960dc..00000000 --- a/src/main/webapp/app/admin/configuration/configuration.service.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable({ providedIn: 'root' }) -export class JhiConfigurationService { - constructor(private http: HttpClient) {} - - get(): Observable { - return this.http.get(SERVER_API_URL + 'management/configprops', { observe: 'response' }).pipe( - map((res: HttpResponse) => { - const properties: any[] = []; - const propertiesObject = this.getConfigPropertiesObjects(res.body); - for (const key in propertiesObject) { - if (propertiesObject.hasOwnProperty(key)) { - properties.push(propertiesObject[key]); - } - } - - return properties.sort((propertyA, propertyB) => { - return propertyA.prefix === propertyB.prefix ? 0 : propertyA.prefix < propertyB.prefix ? -1 : 1; - }); - }) - ); - } - - getConfigPropertiesObjects(res: Object) { - // This code is for Spring Boot 2 - if (res['contexts'] !== undefined) { - for (const key in res['contexts']) { - // If the key is not bootstrap, it will be the ApplicationContext Id - // For default app, it is baseName - // For microservice, it is baseName-1 - if (!key.startsWith('bootstrap')) { - return res['contexts'][key]['beans']; - } - } - } - // by default, use the default ApplicationContext Id - return res['contexts']['hsadminNg']['beans']; - } - - getEnv(): Observable { - return this.http.get(SERVER_API_URL + 'management/env', { observe: 'response' }).pipe( - map((res: HttpResponse) => { - const properties: any = {}; - const propertySources = res.body['propertySources']; - - for (const propertyObject of propertySources) { - const name = propertyObject['name']; - const detailProperties = propertyObject['properties']; - const vals: any[] = []; - for (const keyDetail in detailProperties) { - if (detailProperties.hasOwnProperty(keyDetail)) { - vals.push({ key: keyDetail, val: detailProperties[keyDetail]['value'] }); - } - } - properties[name] = vals; - } - return properties; - }) - ); - } -} diff --git a/src/main/webapp/app/admin/docs/docs.component.html b/src/main/webapp/app/admin/docs/docs.component.html deleted file mode 100644 index 30efbbb9..00000000 --- a/src/main/webapp/app/admin/docs/docs.component.html +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/src/main/webapp/app/admin/docs/docs.component.ts b/src/main/webapp/app/admin/docs/docs.component.ts deleted file mode 100644 index b338e7c3..00000000 --- a/src/main/webapp/app/admin/docs/docs.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'jhi-docs', - templateUrl: './docs.component.html' -}) -export class JhiDocsComponent { - constructor() {} -} diff --git a/src/main/webapp/app/admin/docs/docs.route.ts b/src/main/webapp/app/admin/docs/docs.route.ts deleted file mode 100644 index 9a3a3f80..00000000 --- a/src/main/webapp/app/admin/docs/docs.route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Route } from '@angular/router'; - -import { JhiDocsComponent } from './docs.component'; - -export const docsRoute: Route = { - path: 'docs', - component: JhiDocsComponent, - data: { - pageTitle: 'global.menu.admin.apidocs' - } -}; diff --git a/src/main/webapp/app/admin/health/health-modal.component.html b/src/main/webapp/app/admin/health/health-modal.component.html deleted file mode 100644 index 4f698460..00000000 --- a/src/main/webapp/app/admin/health/health-modal.component.html +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/src/main/webapp/app/admin/health/health-modal.component.ts b/src/main/webapp/app/admin/health/health-modal.component.ts deleted file mode 100644 index 28128bf3..00000000 --- a/src/main/webapp/app/admin/health/health-modal.component.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Component } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; - -import { JhiHealthService } from './health.service'; - -@Component({ - selector: 'jhi-health-modal', - templateUrl: './health-modal.component.html' -}) -export class JhiHealthModalComponent { - currentHealth: any; - - constructor(private healthService: JhiHealthService, public activeModal: NgbActiveModal) {} - - baseName(name) { - return this.healthService.getBaseName(name); - } - - subSystemName(name) { - return this.healthService.getSubSystemName(name); - } - - readableValue(value: number) { - if (this.currentHealth.name === 'diskSpace') { - // Should display storage space in an human readable unit - const val = value / 1073741824; - if (val > 1) { - // Value - return val.toFixed(2) + ' GB'; - } else { - return (value / 1048576).toFixed(2) + ' MB'; - } - } - - if (typeof value === 'object') { - return JSON.stringify(value); - } else { - return value.toString(); - } - } -} diff --git a/src/main/webapp/app/admin/health/health.component.html b/src/main/webapp/app/admin/health/health.component.html deleted file mode 100644 index c17bde9a..00000000 --- a/src/main/webapp/app/admin/health/health.component.html +++ /dev/null @@ -1,34 +0,0 @@ -
-

- Health Checks - -

-
- - - - - - - - - - - - - - - -
Service NameStatusDetails
{{'health.indicator.' + baseName(health.name) | translate}} {{subSystemName(health.name)}} - - {{health.status}} - - - - - -
-
-
diff --git a/src/main/webapp/app/admin/health/health.component.ts b/src/main/webapp/app/admin/health/health.component.ts deleted file mode 100644 index ada3ef62..00000000 --- a/src/main/webapp/app/admin/health/health.component.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; - -import { JhiHealthService } from './health.service'; -import { JhiHealthModalComponent } from './health-modal.component'; - -@Component({ - selector: 'jhi-health', - templateUrl: './health.component.html' -}) -export class JhiHealthCheckComponent implements OnInit { - healthData: any; - updatingHealth: boolean; - - constructor(private modalService: NgbModal, private healthService: JhiHealthService) {} - - ngOnInit() { - this.refresh(); - } - - baseName(name: string) { - return this.healthService.getBaseName(name); - } - - getBadgeClass(statusState) { - if (statusState === 'UP') { - return 'badge-success'; - } else { - return 'badge-danger'; - } - } - - refresh() { - this.updatingHealth = true; - - this.healthService.checkHealth().subscribe( - health => { - this.healthData = this.healthService.transformHealthData(health); - this.updatingHealth = false; - }, - error => { - if (error.status === 503) { - this.healthData = this.healthService.transformHealthData(error.error); - this.updatingHealth = false; - } - } - ); - } - - showHealth(health: any) { - const modalRef = this.modalService.open(JhiHealthModalComponent); - modalRef.componentInstance.currentHealth = health; - modalRef.result.then( - result => { - // Left blank intentionally, nothing to do here - }, - reason => { - // Left blank intentionally, nothing to do here - } - ); - } - - subSystemName(name: string) { - return this.healthService.getSubSystemName(name); - } -} diff --git a/src/main/webapp/app/admin/health/health.route.ts b/src/main/webapp/app/admin/health/health.route.ts deleted file mode 100644 index df801e0c..00000000 --- a/src/main/webapp/app/admin/health/health.route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Route } from '@angular/router'; - -import { JhiHealthCheckComponent } from './health.component'; - -export const healthRoute: Route = { - path: 'jhi-health', - component: JhiHealthCheckComponent, - data: { - pageTitle: 'health.title' - } -}; diff --git a/src/main/webapp/app/admin/health/health.service.ts b/src/main/webapp/app/admin/health/health.service.ts deleted file mode 100644 index 4c1b0e5e..00000000 --- a/src/main/webapp/app/admin/health/health.service.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable({ providedIn: 'root' }) -export class JhiHealthService { - separator: string; - - constructor(private http: HttpClient) { - this.separator = '.'; - } - - checkHealth(): Observable { - return this.http.get(SERVER_API_URL + 'management/health'); - } - - transformHealthData(data): any { - const response = []; - this.flattenHealthData(response, null, data.details); - return response; - } - - getBaseName(name): string { - if (name) { - const split = name.split('.'); - return split[0]; - } - } - - getSubSystemName(name): string { - if (name) { - const split = name.split('.'); - split.splice(0, 1); - const remainder = split.join('.'); - return remainder ? ' - ' + remainder : ''; - } - } - - /* private methods */ - private addHealthObject(result, isLeaf, healthObject, name): any { - const healthData: any = { - name - }; - - const details = {}; - let hasDetails = false; - - for (const key in healthObject) { - if (healthObject.hasOwnProperty(key)) { - const value = healthObject[key]; - if (key === 'status' || key === 'error') { - healthData[key] = value; - } else { - if (!this.isHealthObject(value)) { - details[key] = value; - hasDetails = true; - } - } - } - } - - // Add the details - if (hasDetails) { - healthData.details = details; - } - - // Only add nodes if they provide additional information - if (isLeaf || hasDetails || healthData.error) { - result.push(healthData); - } - return healthData; - } - - private flattenHealthData(result, path, data): any { - for (const key in data) { - if (data.hasOwnProperty(key)) { - const value = data[key]; - if (this.isHealthObject(value)) { - if (this.hasSubSystem(value)) { - this.addHealthObject(result, false, value, this.getModuleName(path, key)); - this.flattenHealthData(result, this.getModuleName(path, key), value); - } else { - this.addHealthObject(result, true, value, this.getModuleName(path, key)); - } - } - } - } - return result; - } - - private getModuleName(path, name): string { - let result; - if (path && name) { - result = path + this.separator + name; - } else if (path) { - result = path; - } else if (name) { - result = name; - } else { - result = ''; - } - return result; - } - - private hasSubSystem(healthObject): boolean { - let result = false; - - for (const key in healthObject) { - if (healthObject.hasOwnProperty(key)) { - const value = healthObject[key]; - if (value && value.status) { - result = true; - } - } - } - return result; - } - - private isHealthObject(healthObject): boolean { - let result = false; - - for (const key in healthObject) { - if (healthObject.hasOwnProperty(key)) { - if (key === 'status') { - result = true; - } - } - } - return result; - } -} diff --git a/src/main/webapp/app/admin/index.ts b/src/main/webapp/app/admin/index.ts deleted file mode 100644 index 7f631ffb..00000000 --- a/src/main/webapp/app/admin/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -export * from './audits/audits.component'; -export * from './audits/audits.service'; -export * from './audits/audits.route'; -export * from './audits/audit.model'; -export * from './audits/audit-data.model'; -export * from './configuration/configuration.component'; -export * from './configuration/configuration.service'; -export * from './configuration/configuration.route'; -export * from './docs/docs.component'; -export * from './docs/docs.route'; -export * from './health/health.component'; -export * from './health/health-modal.component'; -export * from './health/health.service'; -export * from './health/health.route'; -export * from './logs/logs.component'; -export * from './logs/logs.service'; -export * from './logs/logs.route'; -export * from './logs/log.model'; -export * from './metrics/metrics.component'; -export * from './metrics/metrics.service'; -export * from './metrics/metrics.route'; -export * from './user-management/user-management-update.component'; -export * from './user-management/user-management-delete-dialog.component'; -export * from './user-management/user-management-detail.component'; -export * from './user-management/user-management.component'; -export * from './user-management/user-management.route'; -export * from './admin.route'; diff --git a/src/main/webapp/app/admin/logs/log.model.ts b/src/main/webapp/app/admin/logs/log.model.ts deleted file mode 100644 index 3f27b672..00000000 --- a/src/main/webapp/app/admin/logs/log.model.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Log { - constructor(public name: string, public level: string) {} -} diff --git a/src/main/webapp/app/admin/logs/logs.component.html b/src/main/webapp/app/admin/logs/logs.component.html deleted file mode 100644 index f7d8f363..00000000 --- a/src/main/webapp/app/admin/logs/logs.component.html +++ /dev/null @@ -1,28 +0,0 @@ -
-

Logs

- -

There are {{ loggers.length }} loggers.

- - Filter - - - - - - - - - - - - - -
NameLevel
{{logger.name | slice:0:140}} - - - - - - -
-
diff --git a/src/main/webapp/app/admin/logs/logs.component.ts b/src/main/webapp/app/admin/logs/logs.component.ts deleted file mode 100644 index 28547f9a..00000000 --- a/src/main/webapp/app/admin/logs/logs.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { Log } from './log.model'; -import { LogsService } from './logs.service'; - -@Component({ - selector: 'jhi-logs', - templateUrl: './logs.component.html' -}) -export class LogsComponent implements OnInit { - loggers: Log[]; - filter: string; - orderProp: string; - reverse: boolean; - - constructor(private logsService: LogsService) { - this.filter = ''; - this.orderProp = 'name'; - this.reverse = false; - } - - ngOnInit() { - this.logsService.findAll().subscribe(response => (this.loggers = response.body)); - } - - changeLevel(name: string, level: string) { - const log = new Log(name, level); - this.logsService.changeLevel(log).subscribe(() => { - this.logsService.findAll().subscribe(response => (this.loggers = response.body)); - }); - } -} diff --git a/src/main/webapp/app/admin/logs/logs.route.ts b/src/main/webapp/app/admin/logs/logs.route.ts deleted file mode 100644 index 1de755c7..00000000 --- a/src/main/webapp/app/admin/logs/logs.route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Route } from '@angular/router'; - -import { LogsComponent } from './logs.component'; - -export const logsRoute: Route = { - path: 'logs', - component: LogsComponent, - data: { - pageTitle: 'logs.title' - } -}; diff --git a/src/main/webapp/app/admin/logs/logs.service.ts b/src/main/webapp/app/admin/logs/logs.service.ts deleted file mode 100644 index 71a596b0..00000000 --- a/src/main/webapp/app/admin/logs/logs.service.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { Log } from './log.model'; - -@Injectable({ providedIn: 'root' }) -export class LogsService { - constructor(private http: HttpClient) {} - - changeLevel(log: Log): Observable> { - return this.http.put(SERVER_API_URL + 'management/logs', log, { observe: 'response' }); - } - - findAll(): Observable> { - return this.http.get(SERVER_API_URL + 'management/logs', { observe: 'response' }); - } -} diff --git a/src/main/webapp/app/admin/metrics/metrics.component.html b/src/main/webapp/app/admin/metrics/metrics.component.html deleted file mode 100644 index deb1c1b5..00000000 --- a/src/main/webapp/app/admin/metrics/metrics.component.html +++ /dev/null @@ -1,56 +0,0 @@ -
-

- Application Metrics - -

- -

JVM Metrics

-
- - - - - -
- -
-

Garbage collector statistics

- -
- -
Updating...
- - - - -
- - - - - - - - - -
diff --git a/src/main/webapp/app/admin/metrics/metrics.component.ts b/src/main/webapp/app/admin/metrics/metrics.component.ts deleted file mode 100644 index ed508c81..00000000 --- a/src/main/webapp/app/admin/metrics/metrics.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; - -import { JhiMetricsService } from './metrics.service'; - -@Component({ - selector: 'jhi-metrics', - templateUrl: './metrics.component.html' -}) -export class JhiMetricsMonitoringComponent implements OnInit { - metrics: any = {}; - threadData: any = {}; - updatingMetrics = true; - JCACHE_KEY: string; - - constructor(private modalService: NgbModal, private metricsService: JhiMetricsService) { - this.JCACHE_KEY = 'jcache.statistics'; - } - - ngOnInit() { - this.refresh(); - } - - refresh() { - this.updatingMetrics = true; - this.metricsService.getMetrics().subscribe(metrics => { - this.metrics = metrics; - this.metricsService.threadDump().subscribe(data => { - this.threadData = data.threads; - this.updatingMetrics = false; - }); - }); - } - - isObjectExisting(metrics: any, key: string) { - return metrics && metrics[key]; - } - - isObjectExistingAndNotEmpty(metrics: any, key: string) { - return this.isObjectExisting(metrics, key) && JSON.stringify(metrics[key]) !== '{}'; - } -} diff --git a/src/main/webapp/app/admin/metrics/metrics.route.ts b/src/main/webapp/app/admin/metrics/metrics.route.ts deleted file mode 100644 index ba8f5936..00000000 --- a/src/main/webapp/app/admin/metrics/metrics.route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Route } from '@angular/router'; - -import { JhiMetricsMonitoringComponent } from './metrics.component'; - -export const metricsRoute: Route = { - path: 'jhi-metrics', - component: JhiMetricsMonitoringComponent, - data: { - pageTitle: 'metrics.title' - } -}; diff --git a/src/main/webapp/app/admin/metrics/metrics.service.ts b/src/main/webapp/app/admin/metrics/metrics.service.ts deleted file mode 100644 index 15cfe353..00000000 --- a/src/main/webapp/app/admin/metrics/metrics.service.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable({ providedIn: 'root' }) -export class JhiMetricsService { - constructor(private http: HttpClient) {} - - getMetrics(): Observable { - return this.http.get(SERVER_API_URL + 'management/jhi-metrics'); - } - - threadDump(): Observable { - return this.http.get(SERVER_API_URL + 'management/threaddump'); - } -} diff --git a/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html b/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html deleted file mode 100644 index 7e61bd5f..00000000 --- a/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
- - - -
diff --git a/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts b/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts deleted file mode 100644 index d7674f6c..00000000 --- a/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Component } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { JhiEventManager } from 'ng-jhipster'; - -import { User, UserService } from 'app/core'; - -@Component({ - selector: 'jhi-user-mgmt-delete-dialog', - templateUrl: './user-management-delete-dialog.component.html' -}) -export class UserMgmtDeleteDialogComponent { - user: User; - - constructor(private userService: UserService, public activeModal: NgbActiveModal, private eventManager: JhiEventManager) {} - - clear() { - this.activeModal.dismiss('cancel'); - } - - confirmDelete(login) { - this.userService.delete(login).subscribe(response => { - this.eventManager.broadcast({ - name: 'userListModification', - content: 'Deleted a user' - }); - this.activeModal.dismiss(true); - }); - } -} diff --git a/src/main/webapp/app/admin/user-management/user-management-detail.component.html b/src/main/webapp/app/admin/user-management/user-management-detail.component.html deleted file mode 100644 index 0fee91b9..00000000 --- a/src/main/webapp/app/admin/user-management/user-management-detail.component.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
-
-

- User [{{user.login}}] -

-
-
Login
-
- {{user.login}} - - -
-
First Name
-
{{user.firstName}}
-
Last Name
-
{{user.lastName}}
-
Email
-
{{user.email}}
-
Lang Key
-
{{user.langKey}}
-
Created By
-
{{user.createdBy}}
-
Created Date
-
{{user.createdDate | date:'dd/MM/yy HH:mm' }}
-
Last Modified By
-
{{user.lastModifiedBy}}
-
Last Modified Date
-
{{user.lastModifiedDate | date:'dd/MM/yy HH:mm'}}
-
Profiles
-
-
    -
  • - {{authority}} -
  • -
-
-
- -
-
-
diff --git a/src/main/webapp/app/admin/user-management/user-management-detail.component.ts b/src/main/webapp/app/admin/user-management/user-management-detail.component.ts deleted file mode 100644 index 0b323d89..00000000 --- a/src/main/webapp/app/admin/user-management/user-management-detail.component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { User } from 'app/core'; - -@Component({ - selector: 'jhi-user-mgmt-detail', - templateUrl: './user-management-detail.component.html' -}) -export class UserMgmtDetailComponent implements OnInit { - user: User; - - constructor(private route: ActivatedRoute) {} - - ngOnInit() { - this.route.data.subscribe(({ user }) => { - this.user = user.body ? user.body : user; - }); - } -} diff --git a/src/main/webapp/app/admin/user-management/user-management-update.component.html b/src/main/webapp/app/admin/user-management/user-management-update.component.html deleted file mode 100644 index 91596c7c..00000000 --- a/src/main/webapp/app/admin/user-management/user-management-update.component.html +++ /dev/null @@ -1,124 +0,0 @@ -
-
-
-

- Create or edit a User -

-
- -
- - -
- -
- - - -
- - This field is required. - - - - This field cannot be longer than 50 characters. - - - - This field can only contain letters, digits and e-mail addresses. - -
-
-
- - - -
- - This field cannot be longer than 50 characters. - -
-
-
- - - -
- - This field cannot be longer than 50 characters. - -
-
-
- - - -
- - This field is required. - - - - This field cannot be longer than 100 characters. - - - - This field is required to be at least 5 characters. - - - - Your email is invalid. - -
-
-
- -
- -
- - -
-
- - -
-
-
- - -
-
-
-
diff --git a/src/main/webapp/app/admin/user-management/user-management-update.component.ts b/src/main/webapp/app/admin/user-management/user-management-update.component.ts deleted file mode 100644 index 84e06c5b..00000000 --- a/src/main/webapp/app/admin/user-management/user-management-update.component.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; - -import { JhiLanguageHelper, User, UserService } from 'app/core'; - -@Component({ - selector: 'jhi-user-mgmt-update', - templateUrl: './user-management-update.component.html' -}) -export class UserMgmtUpdateComponent implements OnInit { - user: User; - languages: any[]; - authorities: any[]; - isSaving: boolean; - - constructor( - private languageHelper: JhiLanguageHelper, - private userService: UserService, - private route: ActivatedRoute, - private router: Router - ) {} - - ngOnInit() { - this.isSaving = false; - this.route.data.subscribe(({ user }) => { - this.user = user.body ? user.body : user; - }); - this.authorities = []; - this.userService.authorities().subscribe(authorities => { - this.authorities = authorities; - }); - this.languageHelper.getAll().then(languages => { - this.languages = languages; - }); - } - - previousState() { - window.history.back(); - } - - save() { - this.isSaving = true; - if (this.user.id !== null) { - this.userService.update(this.user).subscribe(response => this.onSaveSuccess(response), () => this.onSaveError()); - } else { - this.userService.create(this.user).subscribe(response => this.onSaveSuccess(response), () => this.onSaveError()); - } - } - - private onSaveSuccess(result) { - this.isSaving = false; - this.previousState(); - } - - private onSaveError() { - this.isSaving = false; - } -} diff --git a/src/main/webapp/app/admin/user-management/user-management.component.html b/src/main/webapp/app/admin/user-management/user-management.component.html deleted file mode 100644 index 9f387b0b..00000000 --- a/src/main/webapp/app/admin/user-management/user-management.component.html +++ /dev/null @@ -1,79 +0,0 @@ -
-

- Users - -

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ID Login Email Lang Key ProfilesCreated Date Last Modified By Last Modified Date
{{user.id}}{{user.login}}{{user.email}} - - - {{user.langKey}} -
- {{ authority }} -
-
{{user.createdDate | date:'dd/MM/yy HH:mm'}}{{user.lastModifiedBy}}{{user.lastModifiedDate | date:'dd/MM/yy HH:mm'}} -
- - - -
-
-
-
-
- -
-
- -
-
-
diff --git a/src/main/webapp/app/admin/user-management/user-management.component.ts b/src/main/webapp/app/admin/user-management/user-management.component.ts deleted file mode 100644 index 439442e3..00000000 --- a/src/main/webapp/app/admin/user-management/user-management.component.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; - -import { ActivatedRoute, Router } from '@angular/router'; -import { JhiEventManager, JhiParseLinks, JhiAlertService } from 'ng-jhipster'; - -import { ITEMS_PER_PAGE } from 'app/shared'; -import { AccountService, UserService, User } from 'app/core'; -import { UserMgmtDeleteDialogComponent } from 'app/admin'; - -@Component({ - selector: 'jhi-user-mgmt', - templateUrl: './user-management.component.html' -}) -export class UserMgmtComponent implements OnInit, OnDestroy { - currentAccount: any; - users: User[]; - error: any; - success: any; - routeData: any; - links: any; - totalItems: any; - itemsPerPage: any; - page: any; - predicate: any; - previousPage: any; - reverse: any; - - constructor( - private userService: UserService, - private alertService: JhiAlertService, - private accountService: AccountService, - private parseLinks: JhiParseLinks, - private activatedRoute: ActivatedRoute, - private router: Router, - private eventManager: JhiEventManager, - private modalService: NgbModal - ) { - this.itemsPerPage = ITEMS_PER_PAGE; - this.routeData = this.activatedRoute.data.subscribe(data => { - this.page = data['pagingParams'].page; - this.previousPage = data['pagingParams'].page; - this.reverse = data['pagingParams'].ascending; - this.predicate = data['pagingParams'].predicate; - }); - } - - ngOnInit() { - this.accountService.identity().then(account => { - this.currentAccount = account; - this.loadAll(); - this.registerChangeInUsers(); - }); - } - - ngOnDestroy() { - this.routeData.unsubscribe(); - } - - registerChangeInUsers() { - this.eventManager.subscribe('userListModification', response => this.loadAll()); - } - - setActive(user, isActivated) { - user.activated = isActivated; - - this.userService.update(user).subscribe(response => { - if (response.status === 200) { - this.error = null; - this.success = 'OK'; - this.loadAll(); - } else { - this.success = null; - this.error = 'ERROR'; - } - }); - } - - loadAll() { - this.userService - .query({ - page: this.page - 1, - size: this.itemsPerPage, - sort: this.sort() - }) - .subscribe( - (res: HttpResponse) => this.onSuccess(res.body, res.headers), - (res: HttpResponse) => this.onError(res.body) - ); - } - - trackIdentity(index, item: User) { - return item.id; - } - - sort() { - const result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; - if (this.predicate !== 'id') { - result.push('id'); - } - return result; - } - - loadPage(page: number) { - if (page !== this.previousPage) { - this.previousPage = page; - this.transition(); - } - } - - transition() { - this.router.navigate(['/admin/user-management'], { - queryParams: { - page: this.page, - sort: this.predicate + ',' + (this.reverse ? 'asc' : 'desc') - } - }); - this.loadAll(); - } - - deleteUser(user: User) { - const modalRef = this.modalService.open(UserMgmtDeleteDialogComponent, { size: 'lg', backdrop: 'static' }); - modalRef.componentInstance.user = user; - modalRef.result.then( - result => { - // Left blank intentionally, nothing to do here - }, - reason => { - // Left blank intentionally, nothing to do here - } - ); - } - - private onSuccess(data, headers) { - this.links = this.parseLinks.parse(headers.get('link')); - this.totalItems = headers.get('X-Total-Count'); - this.users = data; - } - - private onError(error) { - this.alertService.error(error.error, error.message, null); - } -} diff --git a/src/main/webapp/app/admin/user-management/user-management.route.ts b/src/main/webapp/app/admin/user-management/user-management.route.ts deleted file mode 100644 index b02939b7..00000000 --- a/src/main/webapp/app/admin/user-management/user-management.route.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes, CanActivate } from '@angular/router'; -import { JhiPaginationUtil, JhiResolvePagingParams } from 'ng-jhipster'; - -import { AccountService, User, UserService } from 'app/core'; -import { UserMgmtComponent } from './user-management.component'; -import { UserMgmtDetailComponent } from './user-management-detail.component'; -import { UserMgmtUpdateComponent } from './user-management-update.component'; - -@Injectable({ providedIn: 'root' }) -export class UserResolve implements CanActivate { - constructor(private accountService: AccountService) {} - - canActivate() { - return this.accountService.identity().then(account => this.accountService.hasAnyAuthority(['ROLE_ADMIN'])); - } -} - -@Injectable({ providedIn: 'root' }) -export class UserMgmtResolve implements Resolve { - constructor(private service: UserService) {} - - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { - const id = route.params['login'] ? route.params['login'] : null; - if (id) { - return this.service.find(id); - } - return new User(); - } -} - -export const userMgmtRoute: Routes = [ - { - path: 'user-management', - component: UserMgmtComponent, - resolve: { - pagingParams: JhiResolvePagingParams - }, - data: { - pageTitle: 'userManagement.home.title', - defaultSort: 'id,asc' - } - }, - { - path: 'user-management/:login/view', - component: UserMgmtDetailComponent, - resolve: { - user: UserMgmtResolve - }, - data: { - pageTitle: 'userManagement.home.title' - } - }, - { - path: 'user-management/new', - component: UserMgmtUpdateComponent, - resolve: { - user: UserMgmtResolve - } - }, - { - path: 'user-management/:login/edit', - component: UserMgmtUpdateComponent, - resolve: { - user: UserMgmtResolve - } - } -]; diff --git a/src/main/webapp/app/app-routing.module.ts b/src/main/webapp/app/app-routing.module.ts deleted file mode 100644 index affde551..00000000 --- a/src/main/webapp/app/app-routing.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { errorRoute, navbarRoute } from './layouts'; -import { DEBUG_INFO_ENABLED } from 'app/app.constants'; - -const LAYOUT_ROUTES = [navbarRoute, ...errorRoute]; - -@NgModule({ - imports: [ - RouterModule.forRoot( - [ - { - path: 'admin', - loadChildren: './admin/admin.module#HsadminNgAdminModule' - }, - ...LAYOUT_ROUTES - ], - { useHash: true, enableTracing: DEBUG_INFO_ENABLED } - ) - ], - exports: [RouterModule] -}) -export class HsadminNgAppRoutingModule {} diff --git a/src/main/webapp/app/app.constants.ts b/src/main/webapp/app/app.constants.ts deleted file mode 100644 index 9760a49a..00000000 --- a/src/main/webapp/app/app.constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -// These constants are injected via webpack environment variables. -// You can add more variables in webpack.common.js or in profile specific webpack..js files. -// If you change the values in the webpack config files, you need to re run webpack to update the application - -export const VERSION = process.env.VERSION; -export const DEBUG_INFO_ENABLED: boolean = !!process.env.DEBUG_INFO_ENABLED; -export const SERVER_API_URL = process.env.SERVER_API_URL; -export const BUILD_TIMESTAMP = process.env.BUILD_TIMESTAMP; diff --git a/src/main/webapp/app/app.main.ts b/src/main/webapp/app/app.main.ts deleted file mode 100644 index 77795715..00000000 --- a/src/main/webapp/app/app.main.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { ProdConfig } from './blocks/config/prod.config'; -import { HsadminNgAppModule } from './app.module'; - -ProdConfig(); - -if (module['hot']) { - module['hot'].accept(); -} - -platformBrowserDynamic() - .bootstrapModule(HsadminNgAppModule, { preserveWhitespaces: true }) - .then(success => console.log(`Application started`)) - .catch(err => console.error(err)); diff --git a/src/main/webapp/app/app.module.ts b/src/main/webapp/app/app.module.ts deleted file mode 100644 index d8ed4b4c..00000000 --- a/src/main/webapp/app/app.module.ts +++ /dev/null @@ -1,72 +0,0 @@ -import './vendor.ts'; - -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { NgbDatepickerConfig } from '@ng-bootstrap/ng-bootstrap'; -import { Ng2Webstorage } from 'ngx-webstorage'; -import { NgJhipsterModule } from 'ng-jhipster'; - -import { AuthInterceptor } from './blocks/interceptor/auth.interceptor'; -import { AuthExpiredInterceptor } from './blocks/interceptor/auth-expired.interceptor'; -import { ErrorHandlerInterceptor } from './blocks/interceptor/errorhandler.interceptor'; -import { NotificationInterceptor } from './blocks/interceptor/notification.interceptor'; -import { HsadminNgSharedModule } from 'app/shared'; -import { HsadminNgCoreModule } from 'app/core'; -import { HsadminNgAppRoutingModule } from './app-routing.module'; -import { HsadminNgHomeModule } from './home/home.module'; -import { HsadminNgAccountModule } from './account/account.module'; -import { HsadminNgEntityModule } from './entities/entity.module'; -import * as moment from 'moment'; -// jhipster-needle-angular-add-module-import JHipster will add new module here -import { JhiMainComponent, NavbarComponent, FooterComponent, PageRibbonComponent, ActiveMenuDirective, ErrorComponent } from './layouts'; - -@NgModule({ - imports: [ - BrowserModule, - Ng2Webstorage.forRoot({ prefix: 'jhi', separator: '-' }), - NgJhipsterModule.forRoot({ - // set below to true to make alerts look like toast - alertAsToast: false, - alertTimeout: 5000, - i18nEnabled: true, - defaultI18nLang: 'de' - }), - HsadminNgSharedModule.forRoot(), - HsadminNgCoreModule, - HsadminNgHomeModule, - HsadminNgAccountModule, - // jhipster-needle-angular-add-module JHipster will add new module here - HsadminNgEntityModule, - HsadminNgAppRoutingModule - ], - declarations: [JhiMainComponent, NavbarComponent, ErrorComponent, PageRibbonComponent, ActiveMenuDirective, FooterComponent], - providers: [ - { - provide: HTTP_INTERCEPTORS, - useClass: AuthInterceptor, - multi: true - }, - { - provide: HTTP_INTERCEPTORS, - useClass: AuthExpiredInterceptor, - multi: true - }, - { - provide: HTTP_INTERCEPTORS, - useClass: ErrorHandlerInterceptor, - multi: true - }, - { - provide: HTTP_INTERCEPTORS, - useClass: NotificationInterceptor, - multi: true - } - ], - bootstrap: [JhiMainComponent] -}) -export class HsadminNgAppModule { - constructor(private dpConfig: NgbDatepickerConfig) { - this.dpConfig.minDate = { year: moment().year() - 100, month: 1, day: 1 }; - } -} diff --git a/src/main/webapp/app/blocks/config/prod.config.ts b/src/main/webapp/app/blocks/config/prod.config.ts deleted file mode 100644 index c6221c1e..00000000 --- a/src/main/webapp/app/blocks/config/prod.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { enableProdMode } from '@angular/core'; -import { DEBUG_INFO_ENABLED } from 'app/app.constants'; - -export function ProdConfig() { - // disable debug data on prod profile to improve performance - if (!DEBUG_INFO_ENABLED) { - enableProdMode(); - } -} diff --git a/src/main/webapp/app/blocks/config/uib-pagination.config.ts b/src/main/webapp/app/blocks/config/uib-pagination.config.ts deleted file mode 100644 index 0c2ea948..00000000 --- a/src/main/webapp/app/blocks/config/uib-pagination.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Injectable } from '@angular/core'; -import { NgbPaginationConfig } from '@ng-bootstrap/ng-bootstrap'; -import { ITEMS_PER_PAGE } from 'app/shared'; - -@Injectable({ providedIn: 'root' }) -export class PaginationConfig { - // tslint:disable-next-line: no-unused-variable - constructor(private config: NgbPaginationConfig) { - config.boundaryLinks = true; - config.maxSize = 5; - config.pageSize = ITEMS_PER_PAGE; - config.size = 'sm'; - } -} diff --git a/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts b/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts deleted file mode 100644 index bc1b70cf..00000000 --- a/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { tap } from 'rxjs/operators'; -import { LoginService } from 'app/core/login/login.service'; - -@Injectable() -export class AuthExpiredInterceptor implements HttpInterceptor { - constructor(private loginService: LoginService) {} - - intercept(request: HttpRequest, next: HttpHandler): Observable> { - return next.handle(request).pipe( - tap( - (event: HttpEvent) => {}, - (err: any) => { - if (err instanceof HttpErrorResponse) { - if (err.status === 401) { - this.loginService.logout(); - } - } - } - ) - ); - } -} diff --git a/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts b/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts deleted file mode 100644 index 23cdeaf6..00000000 --- a/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; -import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable() -export class AuthInterceptor implements HttpInterceptor { - constructor(private localStorage: LocalStorageService, private sessionStorage: SessionStorageService) {} - - intercept(request: HttpRequest, next: HttpHandler): Observable> { - if (!request || !request.url || (/^http/.test(request.url) && !(SERVER_API_URL && request.url.startsWith(SERVER_API_URL)))) { - return next.handle(request); - } - - const token = this.localStorage.retrieve('authenticationToken') || this.sessionStorage.retrieve('authenticationToken'); - if (!!token) { - request = request.clone({ - setHeaders: { - Authorization: 'Bearer ' + token - } - }); - } - return next.handle(request); - } -} diff --git a/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts b/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts deleted file mode 100644 index 183feddf..00000000 --- a/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from '@angular/core'; -import { JhiEventManager } from 'ng-jhipster'; -import { HttpInterceptor, HttpRequest, HttpErrorResponse, HttpHandler, HttpEvent } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { tap } from 'rxjs/operators'; - -@Injectable() -export class ErrorHandlerInterceptor implements HttpInterceptor { - constructor(private eventManager: JhiEventManager) {} - - intercept(request: HttpRequest, next: HttpHandler): Observable> { - return next.handle(request).pipe( - tap( - (event: HttpEvent) => {}, - (err: any) => { - if (err instanceof HttpErrorResponse) { - if (!(err.status === 401 && (err.message === '' || (err.url && err.url.includes('/api/account'))))) { - this.eventManager.broadcast({ name: 'hsadminNgApp.httpError', content: err }); - } - } - } - ) - ); - } -} diff --git a/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts b/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts deleted file mode 100644 index 55b2b99b..00000000 --- a/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { JhiAlertService } from 'ng-jhipster'; -import { HttpInterceptor, HttpRequest, HttpResponse, HttpHandler, HttpEvent } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { tap } from 'rxjs/operators'; - -@Injectable() -export class NotificationInterceptor implements HttpInterceptor { - constructor(private alertService: JhiAlertService) {} - - intercept(request: HttpRequest, next: HttpHandler): Observable> { - return next.handle(request).pipe( - tap( - (event: HttpEvent) => { - if (event instanceof HttpResponse) { - const arr = event.headers.keys(); - let alert = null; - let alertParams = null; - arr.forEach(entry => { - if (entry.toLowerCase().endsWith('app-alert')) { - alert = event.headers.get(entry); - } else if (entry.toLowerCase().endsWith('app-params')) { - alertParams = event.headers.get(entry); - } - }); - if (alert) { - if (typeof alert === 'string') { - this.alertService.success(alert, { param: alertParams }, null); - } - } - } - }, - (err: any) => {} - ) - ); - } -} diff --git a/src/main/webapp/app/core/auth/account.service.ts b/src/main/webapp/app/core/auth/account.service.ts deleted file mode 100644 index 047bbfc9..00000000 --- a/src/main/webapp/app/core/auth/account.service.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Injectable } from '@angular/core'; -import { JhiLanguageService } from 'ng-jhipster'; -import { SessionStorageService } from 'ngx-webstorage'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable, Subject } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { Account } from 'app/core/user/account.model'; - -@Injectable({ providedIn: 'root' }) -export class AccountService { - private userIdentity: any; - private authenticated = false; - private authenticationState = new Subject(); - - constructor(private languageService: JhiLanguageService, private sessionStorage: SessionStorageService, private http: HttpClient) {} - - fetch(): Observable> { - return this.http.get(SERVER_API_URL + 'api/account', { observe: 'response' }); - } - - save(account: any): Observable> { - return this.http.post(SERVER_API_URL + 'api/account', account, { observe: 'response' }); - } - - authenticate(identity) { - this.userIdentity = identity; - this.authenticated = identity !== null; - this.authenticationState.next(this.userIdentity); - } - - hasAnyAuthority(authorities: string[]): boolean { - if (!this.authenticated || !this.userIdentity || !this.userIdentity.authorities) { - return false; - } - - for (let i = 0; i < authorities.length; i++) { - if (this.userIdentity.authorities.includes(authorities[i])) { - return true; - } - } - - return false; - } - - hasAuthority(authority: string): Promise { - if (!this.authenticated) { - return Promise.resolve(false); - } - - return this.identity().then( - id => { - return Promise.resolve(id.authorities && id.authorities.includes(authority)); - }, - () => { - return Promise.resolve(false); - } - ); - } - - identity(force?: boolean): Promise { - if (force) { - this.userIdentity = undefined; - } - - // check and see if we have retrieved the userIdentity data from the server. - // if we have, reuse it by immediately resolving - if (this.userIdentity) { - return Promise.resolve(this.userIdentity); - } - - // retrieve the userIdentity data from the server, update the identity object, and then resolve. - return this.fetch() - .toPromise() - .then(response => { - const account = response.body; - if (account) { - this.userIdentity = account; - this.authenticated = true; - // After retrieve the account info, the language will be changed to - // the user's preferred language configured in the account setting - const langKey = this.sessionStorage.retrieve('locale') || this.userIdentity.langKey; - this.languageService.changeLanguage(langKey); - } else { - this.userIdentity = null; - this.authenticated = false; - } - this.authenticationState.next(this.userIdentity); - return this.userIdentity; - }) - .catch(err => { - this.userIdentity = null; - this.authenticated = false; - this.authenticationState.next(this.userIdentity); - return null; - }); - } - - isAuthenticated(): boolean { - return this.authenticated; - } - - isIdentityResolved(): boolean { - return this.userIdentity !== undefined; - } - - getAuthenticationState(): Observable { - return this.authenticationState.asObservable(); - } - - getImageUrl(): string { - return this.isIdentityResolved() ? this.userIdentity.imageUrl : null; - } -} diff --git a/src/main/webapp/app/core/auth/auth-jwt.service.ts b/src/main/webapp/app/core/auth/auth-jwt.service.ts deleted file mode 100644 index 5ad53e3d..00000000 --- a/src/main/webapp/app/core/auth/auth-jwt.service.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; - -import { SERVER_API_URL } from 'app/app.constants'; - -@Injectable({ providedIn: 'root' }) -export class AuthServerProvider { - constructor(private http: HttpClient, private $localStorage: LocalStorageService, private $sessionStorage: SessionStorageService) {} - - getToken() { - return this.$localStorage.retrieve('authenticationToken') || this.$sessionStorage.retrieve('authenticationToken'); - } - - login(credentials): Observable { - const data = { - username: credentials.username, - password: credentials.password, - rememberMe: credentials.rememberMe - }; - return this.http.post(SERVER_API_URL + 'api/authenticate', data, { observe: 'response' }).pipe(map(authenticateSuccess.bind(this))); - - function authenticateSuccess(resp) { - const bearerToken = resp.headers.get('Authorization'); - if (bearerToken && bearerToken.slice(0, 7) === 'Bearer ') { - const jwt = bearerToken.slice(7, bearerToken.length); - this.storeAuthenticationToken(jwt, credentials.rememberMe); - return jwt; - } - } - } - - loginWithToken(jwt, rememberMe) { - if (jwt) { - this.storeAuthenticationToken(jwt, rememberMe); - return Promise.resolve(jwt); - } else { - return Promise.reject('auth-jwt-service Promise reject'); // Put appropriate error message here - } - } - - storeAuthenticationToken(jwt, rememberMe) { - if (rememberMe) { - this.$localStorage.store('authenticationToken', jwt); - } else { - this.$sessionStorage.store('authenticationToken', jwt); - } - } - - logout(): Observable { - return new Observable(observer => { - this.$localStorage.clear('authenticationToken'); - this.$sessionStorage.clear('authenticationToken'); - observer.complete(); - }); - } -} diff --git a/src/main/webapp/app/core/auth/csrf.service.ts b/src/main/webapp/app/core/auth/csrf.service.ts deleted file mode 100644 index 01fdccb0..00000000 --- a/src/main/webapp/app/core/auth/csrf.service.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Injectable } from '@angular/core'; -import { CookieService } from 'ngx-cookie'; - -@Injectable({ providedIn: 'root' }) -export class CSRFService { - constructor(private cookieService: CookieService) {} - - getCSRF(name = 'XSRF-TOKEN') { - return this.cookieService.get(name); - } -} diff --git a/src/main/webapp/app/core/auth/state-storage.service.ts b/src/main/webapp/app/core/auth/state-storage.service.ts deleted file mode 100644 index 0e5befbf..00000000 --- a/src/main/webapp/app/core/auth/state-storage.service.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Injectable } from '@angular/core'; -import { SessionStorageService } from 'ngx-webstorage'; - -@Injectable({ providedIn: 'root' }) -export class StateStorageService { - constructor(private $sessionStorage: SessionStorageService) {} - - getPreviousState() { - return this.$sessionStorage.retrieve('previousState'); - } - - resetPreviousState() { - this.$sessionStorage.clear('previousState'); - } - - storePreviousState(previousStateName, previousStateParams) { - const previousState = { name: previousStateName, params: previousStateParams }; - this.$sessionStorage.store('previousState', previousState); - } - - getDestinationState() { - return this.$sessionStorage.retrieve('destinationState'); - } - - storeUrl(url: string) { - this.$sessionStorage.store('previousUrl', url); - } - - getUrl() { - return this.$sessionStorage.retrieve('previousUrl'); - } - - storeDestinationState(destinationState, destinationStateParams, fromState) { - const destinationInfo = { - destination: { - name: destinationState.name, - data: destinationState.data - }, - params: destinationStateParams, - from: { - name: fromState.name - } - }; - this.$sessionStorage.store('destinationState', destinationInfo); - } -} diff --git a/src/main/webapp/app/core/auth/user-route-access-service.ts b/src/main/webapp/app/core/auth/user-route-access-service.ts deleted file mode 100644 index a55b0bc0..00000000 --- a/src/main/webapp/app/core/auth/user-route-access-service.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Injectable, isDevMode } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; - -import { AccountService } from '../'; -import { LoginModalService } from '../login/login-modal.service'; -import { StateStorageService } from './state-storage.service'; - -@Injectable({ providedIn: 'root' }) -export class UserRouteAccessService implements CanActivate { - constructor( - private router: Router, - private loginModalService: LoginModalService, - private accountService: AccountService, - private stateStorageService: StateStorageService - ) {} - - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Promise { - const authorities = route.data['authorities']; - // We need to call the checkLogin / and so the accountService.identity() function, to ensure, - // that the client has a principal too, if they already logged in by the server. - // This could happen on a page refresh. - return this.checkLogin(authorities, state.url); - } - - checkLogin(authorities: string[], url: string): Promise { - return this.accountService.identity().then(account => { - if (!authorities || authorities.length === 0) { - return true; - } - - if (account) { - const hasAnyAuthority = this.accountService.hasAnyAuthority(authorities); - if (hasAnyAuthority) { - return true; - } - if (isDevMode()) { - console.error('User has not any of required authorities: ', authorities); - } - return false; - } - - this.stateStorageService.storeUrl(url); - this.router.navigate(['accessdenied']).then(() => { - // only show the login dialog, if the user hasn't logged in yet - if (!account) { - this.loginModalService.open(); - } - }); - return false; - }); - } -} diff --git a/src/main/webapp/app/core/core.module.ts b/src/main/webapp/app/core/core.module.ts deleted file mode 100644 index d9a1d0fb..00000000 --- a/src/main/webapp/app/core/core.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NgModule, LOCALE_ID } from '@angular/core'; -import { DatePipe, registerLocaleData } from '@angular/common'; -import { HttpClientModule } from '@angular/common/http'; -import { Title } from '@angular/platform-browser'; -import locale from '@angular/common/locales/de'; - -@NgModule({ - imports: [HttpClientModule], - exports: [], - declarations: [], - providers: [ - Title, - { - provide: LOCALE_ID, - useValue: 'de' - }, - DatePipe - ] -}) -export class HsadminNgCoreModule { - constructor() { - registerLocaleData(locale); - } -} diff --git a/src/main/webapp/app/core/index.ts b/src/main/webapp/app/core/index.ts deleted file mode 100644 index fbfcfd35..00000000 --- a/src/main/webapp/app/core/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from './auth/csrf.service'; -export * from './auth/state-storage.service'; -export * from './auth/account.service'; -export * from './auth/auth-jwt.service'; -export * from './language/language.helper'; -export * from './language/language.constants'; -export * from './user/account.model'; -export * from './user/user.model'; -export * from './auth/user-route-access-service'; -export * from './login/login-modal.service'; -export * from './login/login.service'; -export * from './user/user.service'; -export * from './core.module'; diff --git a/src/main/webapp/app/core/language/language.constants.ts b/src/main/webapp/app/core/language/language.constants.ts deleted file mode 100644 index 15bcff4c..00000000 --- a/src/main/webapp/app/core/language/language.constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - Languages codes are ISO_639-1 codes, see http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes - They are written in English to avoid character encoding issues (not a perfect solution) -*/ -export const LANGUAGES: string[] = [ - 'de', - 'en' - // jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array -]; diff --git a/src/main/webapp/app/core/language/language.helper.ts b/src/main/webapp/app/core/language/language.helper.ts deleted file mode 100644 index 4c0072b1..00000000 --- a/src/main/webapp/app/core/language/language.helper.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Injectable, RendererFactory2, Renderer2 } from '@angular/core'; -import { Title } from '@angular/platform-browser'; -import { Router, ActivatedRouteSnapshot } from '@angular/router'; -import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; -import { BehaviorSubject, Observable } from 'rxjs'; - -import { LANGUAGES } from 'app/core/language/language.constants'; - -@Injectable({ providedIn: 'root' }) -export class JhiLanguageHelper { - renderer: Renderer2 = null; - private _language: BehaviorSubject; - - constructor( - private translateService: TranslateService, - private titleService: Title, - private router: Router, - rootRenderer: RendererFactory2 - ) { - this._language = new BehaviorSubject(this.translateService.currentLang); - this.renderer = rootRenderer.createRenderer(document.querySelector('html'), null); - this.init(); - } - - getAll(): Promise { - return Promise.resolve(LANGUAGES); - } - - get language(): Observable { - return this._language.asObservable(); - } - - /** - * Update the window title using params in the following - * order: - * 1. titleKey parameter - * 2. $state.$current.data.pageTitle (current state page title) - * 3. 'global.title' - */ - updateTitle(titleKey?: string) { - if (!titleKey) { - titleKey = this.getPageTitle(this.router.routerState.snapshot.root); - } - - this.translateService.get(titleKey).subscribe(title => { - this.titleService.setTitle(title); - }); - } - - private init() { - this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { - this._language.next(this.translateService.currentLang); - this.renderer.setAttribute(document.querySelector('html'), 'lang', this.translateService.currentLang); - this.updateTitle(); - }); - } - - private getPageTitle(routeSnapshot: ActivatedRouteSnapshot) { - let title: string = routeSnapshot.data && routeSnapshot.data['pageTitle'] ? routeSnapshot.data['pageTitle'] : 'hsadminNgApp'; - if (routeSnapshot.firstChild) { - title = this.getPageTitle(routeSnapshot.firstChild) || title; - } - return title; - } -} diff --git a/src/main/webapp/app/core/login/login-modal.service.ts b/src/main/webapp/app/core/login/login-modal.service.ts deleted file mode 100644 index a0002aa5..00000000 --- a/src/main/webapp/app/core/login/login-modal.service.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Injectable } from '@angular/core'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; - -import { JhiLoginModalComponent } from 'app/shared/login/login.component'; - -@Injectable({ providedIn: 'root' }) -export class LoginModalService { - private isOpen = false; - constructor(private modalService: NgbModal) {} - - open(): NgbModalRef { - if (this.isOpen) { - return; - } - this.isOpen = true; - const modalRef = this.modalService.open(JhiLoginModalComponent); - modalRef.result.then( - result => { - this.isOpen = false; - }, - reason => { - this.isOpen = false; - } - ); - return modalRef; - } -} diff --git a/src/main/webapp/app/core/login/login.service.ts b/src/main/webapp/app/core/login/login.service.ts deleted file mode 100644 index e91508ff..00000000 --- a/src/main/webapp/app/core/login/login.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable } from '@angular/core'; - -import { AccountService } from 'app/core/auth/account.service'; -import { AuthServerProvider } from 'app/core/auth/auth-jwt.service'; - -@Injectable({ providedIn: 'root' }) -export class LoginService { - constructor(private accountService: AccountService, private authServerProvider: AuthServerProvider) {} - - login(credentials, callback?) { - const cb = callback || function() {}; - - return new Promise((resolve, reject) => { - this.authServerProvider.login(credentials).subscribe( - data => { - this.accountService.identity(true).then(account => { - resolve(data); - }); - return cb(); - }, - err => { - this.logout(); - reject(err); - return cb(err); - } - ); - }); - } - - loginWithToken(jwt, rememberMe) { - return this.authServerProvider.loginWithToken(jwt, rememberMe); - } - - logout() { - this.authServerProvider.logout().subscribe(); - this.accountService.authenticate(null); - } -} diff --git a/src/main/webapp/app/core/user/account.model.ts b/src/main/webapp/app/core/user/account.model.ts deleted file mode 100644 index 35679657..00000000 --- a/src/main/webapp/app/core/user/account.model.ts +++ /dev/null @@ -1,12 +0,0 @@ -export class Account { - constructor( - public activated: boolean, - public authorities: string[], - public email: string, - public firstName: string, - public langKey: string, - public lastName: string, - public login: string, - public imageUrl: string - ) {} -} diff --git a/src/main/webapp/app/core/user/user.model.ts b/src/main/webapp/app/core/user/user.model.ts deleted file mode 100644 index e82da11a..00000000 --- a/src/main/webapp/app/core/user/user.model.ts +++ /dev/null @@ -1,47 +0,0 @@ -export interface IUser { - id?: any; - login?: string; - firstName?: string; - lastName?: string; - email?: string; - activated?: boolean; - langKey?: string; - authorities?: any[]; - createdBy?: string; - createdDate?: Date; - lastModifiedBy?: string; - lastModifiedDate?: Date; - password?: string; -} - -export class User implements IUser { - constructor( - public id?: any, - public login?: string, - public firstName?: string, - public lastName?: string, - public email?: string, - public activated?: boolean, - public langKey?: string, - public authorities?: any[], - public createdBy?: string, - public createdDate?: Date, - public lastModifiedBy?: string, - public lastModifiedDate?: Date, - public password?: string - ) { - this.id = id ? id : null; - this.login = login ? login : null; - this.firstName = firstName ? firstName : null; - this.lastName = lastName ? lastName : null; - this.email = email ? email : null; - this.activated = activated ? activated : false; - this.langKey = langKey ? langKey : null; - this.authorities = authorities ? authorities : null; - this.createdBy = createdBy ? createdBy : null; - this.createdDate = createdDate ? createdDate : null; - this.lastModifiedBy = lastModifiedBy ? lastModifiedBy : null; - this.lastModifiedDate = lastModifiedDate ? lastModifiedDate : null; - this.password = password ? password : null; - } -} diff --git a/src/main/webapp/app/core/user/user.service.ts b/src/main/webapp/app/core/user/user.service.ts deleted file mode 100644 index 5c8065be..00000000 --- a/src/main/webapp/app/core/user/user.service.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { createRequestOption } from 'app/shared/util/request-util'; -import { IUser } from './user.model'; - -@Injectable({ providedIn: 'root' }) -export class UserService { - public resourceUrl = SERVER_API_URL + 'api/users'; - - constructor(private http: HttpClient) {} - - create(user: IUser): Observable> { - return this.http.post(this.resourceUrl, user, { observe: 'response' }); - } - - update(user: IUser): Observable> { - return this.http.put(this.resourceUrl, user, { observe: 'response' }); - } - - find(login: string): Observable> { - return this.http.get(`${this.resourceUrl}/${login}`, { observe: 'response' }); - } - - query(req?: any): Observable> { - const options = createRequestOption(req); - return this.http.get(this.resourceUrl, { params: options, observe: 'response' }); - } - - delete(login: string): Observable> { - return this.http.delete(`${this.resourceUrl}/${login}`, { observe: 'response' }); - } - - authorities(): Observable { - return this.http.get(SERVER_API_URL + 'api/users/authorities'); - } -} diff --git a/src/main/webapp/app/entities/asset/asset-delete-dialog.component.html b/src/main/webapp/app/entities/asset/asset-delete-dialog.component.html deleted file mode 100644 index bd1b9f78..00000000 --- a/src/main/webapp/app/entities/asset/asset-delete-dialog.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
- - - -
diff --git a/src/main/webapp/app/entities/asset/asset-delete-dialog.component.ts b/src/main/webapp/app/entities/asset/asset-delete-dialog.component.ts deleted file mode 100644 index ff1dd113..00000000 --- a/src/main/webapp/app/entities/asset/asset-delete-dialog.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; - -import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { JhiEventManager } from 'ng-jhipster'; - -import { IAsset } from 'app/shared/model/asset.model'; -import { AssetService } from './asset.service'; - -@Component({ - selector: 'jhi-asset-delete-dialog', - templateUrl: './asset-delete-dialog.component.html' -}) -export class AssetDeleteDialogComponent { - asset: IAsset; - - constructor(protected assetService: AssetService, public activeModal: NgbActiveModal, protected eventManager: JhiEventManager) {} - - clear() { - this.activeModal.dismiss('cancel'); - } - - confirmDelete(id: number) { - this.assetService.delete(id).subscribe(response => { - this.eventManager.broadcast({ - name: 'assetListModification', - content: 'Deleted an asset' - }); - this.activeModal.dismiss(true); - }); - } -} - -@Component({ - selector: 'jhi-asset-delete-popup', - template: '' -}) -export class AssetDeletePopupComponent implements OnInit, OnDestroy { - protected ngbModalRef: NgbModalRef; - - constructor(protected activatedRoute: ActivatedRoute, protected router: Router, protected modalService: NgbModal) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ asset }) => { - setTimeout(() => { - this.ngbModalRef = this.modalService.open(AssetDeleteDialogComponent as Component, { size: 'lg', backdrop: 'static' }); - this.ngbModalRef.componentInstance.asset = asset; - this.ngbModalRef.result.then( - result => { - this.router.navigate(['/asset', { outlets: { popup: null } }]); - this.ngbModalRef = null; - }, - reason => { - this.router.navigate(['/asset', { outlets: { popup: null } }]); - this.ngbModalRef = null; - } - ); - }, 0); - }); - } - - ngOnDestroy() { - this.ngbModalRef = null; - } -} diff --git a/src/main/webapp/app/entities/asset/asset-detail.component.html b/src/main/webapp/app/entities/asset/asset-detail.component.html deleted file mode 100644 index f3dd73ed..00000000 --- a/src/main/webapp/app/entities/asset/asset-detail.component.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
-
-

Asset: #{{asset.id}} - {{asset.membershipDisplayLabel}}

-
- -
-
Document Date
-
- {{asset.documentDate | date:'mediumDate'}} -
-
Value Date
-
- {{asset.valueDate | date:'mediumDate'}} -
-
Action
-
- {{asset.action}} -
-
Amount
-
- {{asset.amount}} -
-
Remark
-
- -
-
Membership
-
- -
-
- - - - -
-
-
diff --git a/src/main/webapp/app/entities/asset/asset-detail.component.ts b/src/main/webapp/app/entities/asset/asset-detail.component.ts deleted file mode 100644 index 635ee255..00000000 --- a/src/main/webapp/app/entities/asset/asset-detail.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { IAsset } from 'app/shared/model/asset.model'; - -@Component({ - selector: 'jhi-asset-detail', - templateUrl: './asset-detail.component.html' -}) -export class AssetDetailComponent implements OnInit { - asset: IAsset; - - constructor(protected activatedRoute: ActivatedRoute) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ asset }) => { - this.asset = asset; - }); - } - - previousState() { - window.history.back(); - } -} diff --git a/src/main/webapp/app/entities/asset/asset-update.component.html b/src/main/webapp/app/entities/asset/asset-update.component.html deleted file mode 100644 index d9d36906..00000000 --- a/src/main/webapp/app/entities/asset/asset-update.component.html +++ /dev/null @@ -1,112 +0,0 @@ -
-
-
-

Create or edit a Asset

-
- -
- - -
-
- -
- - - - -
-
- - This field is required. - -
-
-
- -
- - - - -
-
- - This field is required. - -
-
-
- - -
- - This field is required. - -
-
-
- - -
- - This field is required. - - - This field should be a number. - -
-
-
- - -
- - This field cannot be longer than 160 characters. - -
-
- -
- - -
-
- - This field is required. - -
-
-
- - -
-
-
-
diff --git a/src/main/webapp/app/entities/asset/asset-update.component.ts b/src/main/webapp/app/entities/asset/asset-update.component.ts deleted file mode 100644 index a58ab009..00000000 --- a/src/main/webapp/app/entities/asset/asset-update.component.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { HttpResponse, HttpErrorResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import * as moment from 'moment'; -import { JhiAlertService } from 'ng-jhipster'; -import { IAsset } from 'app/shared/model/asset.model'; -import { AssetService } from './asset.service'; -import { IMembership } from 'app/shared/model/membership.model'; -import { MembershipService } from 'app/entities/membership'; - -@Component({ - selector: 'jhi-asset-update', - templateUrl: './asset-update.component.html' -}) -export class AssetUpdateComponent implements OnInit { - asset: IAsset; - isSaving: boolean; - - memberships: IMembership[]; - documentDateDp: any; - valueDateDp: any; - - constructor( - protected jhiAlertService: JhiAlertService, - protected assetService: AssetService, - protected membershipService: MembershipService, - protected activatedRoute: ActivatedRoute - ) {} - - ngOnInit() { - this.isSaving = false; - this.activatedRoute.data.subscribe(({ asset }) => { - this.asset = asset; - }); - this.membershipService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: IMembership[]) => (this.memberships = res), (res: HttpErrorResponse) => this.onError(res.message)); - } - - previousState() { - window.history.back(); - } - - save() { - this.isSaving = true; - if (this.asset.id !== undefined) { - this.subscribeToSaveResponse(this.assetService.update(this.asset)); - } else { - this.subscribeToSaveResponse(this.assetService.create(this.asset)); - } - } - - protected subscribeToSaveResponse(result: Observable>) { - result.subscribe((res: HttpResponse) => this.onSaveSuccess(), (res: HttpErrorResponse) => this.onSaveError()); - } - - protected onSaveSuccess() { - this.isSaving = false; - this.previousState(); - } - - protected onSaveError() { - this.isSaving = false; - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } - - trackMembershipById(index: number, item: IMembership) { - return item.id; - } -} diff --git a/src/main/webapp/app/entities/asset/asset.component.html b/src/main/webapp/app/entities/asset/asset.component.html deleted file mode 100644 index 2545bb6f..00000000 --- a/src/main/webapp/app/entities/asset/asset.component.html +++ /dev/null @@ -1,94 +0,0 @@ -
-

- Assets - -

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ID Document Date Value Date Action Amount Membership
- - - -
{{asset.id}}{{asset.documentDate | date:'mediumDate'}}{{asset.valueDate | date:'mediumDate'}}{{asset.action}}{{asset.amount}} - - -
- - - -
-
-
-
diff --git a/src/main/webapp/app/entities/asset/asset.component.ts b/src/main/webapp/app/entities/asset/asset.component.ts deleted file mode 100644 index 19220504..00000000 --- a/src/main/webapp/app/entities/asset/asset.component.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { Subscription } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { JhiEventManager, JhiParseLinks, JhiAlertService } from 'ng-jhipster'; - -import { IAsset } from 'app/shared/model/asset.model'; -import { AccountService } from 'app/core'; - -import { ITEMS_PER_PAGE } from 'app/shared'; -import { AssetService } from './asset.service'; -import { IMembership } from 'app/shared/model/membership.model'; -import { MembershipService } from 'app/entities/membership'; -import { queryEquals, queryYearAsDateRange, TableFilter } from 'app/shared/util/tablefilter'; - -@Component({ - selector: 'jhi-asset', - templateUrl: './asset.component.html' -}) -export class AssetComponent implements OnInit, OnDestroy { - assets: IAsset[]; - currentAccount: any; - eventSubscriber: Subscription; - itemsPerPage: number; - links: any; - page: any; - predicate: any; - reverse: any; - totalItems: number; - memberships: IMembership[]; - filter: TableFilter<{ - documentDate?: string; - valueDate?: string; - action?: string; - amount?: string; - membershipId?: string; - }>; - - constructor( - protected assetService: AssetService, - protected membershipService: MembershipService, - protected jhiAlertService: JhiAlertService, - protected eventManager: JhiEventManager, - protected parseLinks: JhiParseLinks, - protected accountService: AccountService - ) { - this.assets = []; - this.itemsPerPage = ITEMS_PER_PAGE; - this.page = 0; - this.links = { - last: 0 - }; - this.predicate = 'id'; - this.reverse = true; - this.filter = new TableFilter( - { - documentDate: queryYearAsDateRange, - valueDate: queryYearAsDateRange, - action: queryEquals, - amount: queryEquals, - membershipId: queryEquals - }, - 500, - () => { - this.reset(); - } - ); - } - - loadAll() { - this.assetService - .query({ - ...this.filter.buildQueryCriteria(), - page: this.page, - size: this.itemsPerPage, - sort: this.sort() - }) - .subscribe( - (res: HttpResponse) => this.paginateAssets(res.body, res.headers), - (res: HttpErrorResponse) => this.onError(res.message) - ); - } - - reset() { - this.page = 0; - this.assets = []; - this.loadAll(); - } - - loadPage(page) { - this.page = page; - this.loadAll(); - } - - ngOnInit() { - this.loadAll(); - this.accountService.identity().then(account => { - this.currentAccount = account; - }); - this.membershipService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: IMembership[]) => (this.memberships = res), (res: HttpErrorResponse) => this.onError(res.message)); - this.registerChangeInAssets(); - } - - ngOnDestroy() { - this.eventManager.destroy(this.eventSubscriber); - } - - trackId(index: number, item: { id: number }) { - return item.id; - } - - registerChangeInAssets() { - this.eventSubscriber = this.eventManager.subscribe('assetListModification', response => this.reset()); - } - - sort() { - const result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; - if (this.predicate !== 'id') { - result.push('id'); - } - return result; - } - - protected paginateAssets(data: IAsset[], headers: HttpHeaders) { - this.links = this.parseLinks.parse(headers.get('link')); - this.totalItems = parseInt(headers.get('X-Total-Count'), 10); - for (let i = 0; i < data.length; i++) { - this.assets.push(data[i]); - } - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } -} diff --git a/src/main/webapp/app/entities/asset/asset.module.ts b/src/main/webapp/app/entities/asset/asset.module.ts deleted file mode 100644 index 282a44e4..00000000 --- a/src/main/webapp/app/entities/asset/asset.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { JhiLanguageService } from 'ng-jhipster'; -import { JhiLanguageHelper } from 'app/core'; - -import { HsadminNgSharedModule } from 'app/shared'; -import { - AssetComponent, - AssetDetailComponent, - AssetUpdateComponent, - AssetDeletePopupComponent, - AssetDeleteDialogComponent, - assetRoute, - assetPopupRoute -} from './'; - -const ENTITY_STATES = [...assetRoute, ...assetPopupRoute]; - -@NgModule({ - imports: [HsadminNgSharedModule, RouterModule.forChild(ENTITY_STATES)], - declarations: [AssetComponent, AssetDetailComponent, AssetUpdateComponent, AssetDeleteDialogComponent, AssetDeletePopupComponent], - entryComponents: [AssetComponent, AssetUpdateComponent, AssetDeleteDialogComponent, AssetDeletePopupComponent], - providers: [{ provide: JhiLanguageService, useClass: JhiLanguageService }], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgAssetModule { - constructor(private languageService: JhiLanguageService, private languageHelper: JhiLanguageHelper) { - this.languageHelper.language.subscribe((languageKey: string) => { - if (languageKey !== undefined) { - this.languageService.changeLanguage(languageKey); - } - }); - } -} diff --git a/src/main/webapp/app/entities/asset/asset.route.ts b/src/main/webapp/app/entities/asset/asset.route.ts deleted file mode 100644 index b7e030d8..00000000 --- a/src/main/webapp/app/entities/asset/asset.route.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes } from '@angular/router'; -import { UserRouteAccessService } from 'app/core'; -import { Observable, of } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { Asset } from 'app/shared/model/asset.model'; -import { AssetService } from './asset.service'; -import { AssetComponent } from './asset.component'; -import { AssetDetailComponent } from './asset-detail.component'; -import { AssetUpdateComponent } from './asset-update.component'; -import { AssetDeletePopupComponent } from './asset-delete-dialog.component'; -import { IAsset } from 'app/shared/model/asset.model'; - -@Injectable({ providedIn: 'root' }) -export class AssetResolve implements Resolve { - constructor(private service: AssetService) {} - - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - const id = route.params['id'] ? route.params['id'] : null; - if (id) { - return this.service.find(id).pipe( - filter((response: HttpResponse) => response.ok), - map((asset: HttpResponse) => asset.body) - ); - } - return of(new Asset()); - } -} - -export const assetRoute: Routes = [ - { - path: '', - component: AssetComponent, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.asset.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/view', - component: AssetDetailComponent, - resolve: { - asset: AssetResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.asset.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: 'new', - component: AssetUpdateComponent, - resolve: { - asset: AssetResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.asset.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/edit', - component: AssetUpdateComponent, - resolve: { - asset: AssetResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.asset.home.title' - }, - canActivate: [UserRouteAccessService] - } -]; - -export const assetPopupRoute: Routes = [ - { - path: ':id/delete', - component: AssetDeletePopupComponent, - resolve: { - asset: AssetResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.asset.home.title' - }, - canActivate: [UserRouteAccessService], - outlet: 'popup' - } -]; diff --git a/src/main/webapp/app/entities/asset/asset.service.ts b/src/main/webapp/app/entities/asset/asset.service.ts deleted file mode 100644 index 379d2cec..00000000 --- a/src/main/webapp/app/entities/asset/asset.service.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { map } from 'rxjs/operators'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { createRequestOption } from 'app/shared'; -import { IAsset } from 'app/shared/model/asset.model'; - -type EntityResponseType = HttpResponse; -type EntityArrayResponseType = HttpResponse; - -@Injectable({ providedIn: 'root' }) -export class AssetService { - public resourceUrl = SERVER_API_URL + 'api/assets'; - - constructor(protected http: HttpClient) {} - - create(asset: IAsset): Observable { - const copy = this.convertDateFromClient(asset); - return this.http - .post(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - update(asset: IAsset): Observable { - const copy = this.convertDateFromClient(asset); - return this.http - .put(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - find(id: number): Observable { - return this.http - .get(`${this.resourceUrl}/${id}`, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - query(req?: any): Observable { - const options = createRequestOption(req); - return this.http - .get(this.resourceUrl, { params: options, observe: 'response' }) - .pipe(map((res: EntityArrayResponseType) => this.convertDateArrayFromServer(res))); - } - - delete(id: number): Observable> { - return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); - } - - protected convertDateFromClient(asset: IAsset): IAsset { - const copy: IAsset = Object.assign({}, asset, { - documentDate: asset.documentDate != null && asset.documentDate.isValid() ? asset.documentDate.format(DATE_FORMAT) : null, - valueDate: asset.valueDate != null && asset.valueDate.isValid() ? asset.valueDate.format(DATE_FORMAT) : null - }); - return copy; - } - - protected convertDateFromServer(res: EntityResponseType): EntityResponseType { - if (res.body) { - res.body.documentDate = res.body.documentDate != null ? moment(res.body.documentDate) : null; - res.body.valueDate = res.body.valueDate != null ? moment(res.body.valueDate) : null; - } - return res; - } - - protected convertDateArrayFromServer(res: EntityArrayResponseType): EntityArrayResponseType { - if (res.body) { - res.body.forEach((asset: IAsset) => { - asset.documentDate = asset.documentDate != null ? moment(asset.documentDate) : null; - asset.valueDate = asset.valueDate != null ? moment(asset.valueDate) : null; - }); - } - return res; - } -} diff --git a/src/main/webapp/app/entities/asset/index.ts b/src/main/webapp/app/entities/asset/index.ts deleted file mode 100644 index 5f865f3d..00000000 --- a/src/main/webapp/app/entities/asset/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './asset.service'; -export * from './asset-update.component'; -export * from './asset-delete-dialog.component'; -export * from './asset-detail.component'; -export * from './asset.component'; -export * from './asset.route'; diff --git a/src/main/webapp/app/entities/customer/customer-delete-dialog.component.html b/src/main/webapp/app/entities/customer/customer-delete-dialog.component.html deleted file mode 100644 index b04a8e62..00000000 --- a/src/main/webapp/app/entities/customer/customer-delete-dialog.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
- - - -
diff --git a/src/main/webapp/app/entities/customer/customer-delete-dialog.component.ts b/src/main/webapp/app/entities/customer/customer-delete-dialog.component.ts deleted file mode 100644 index 3c96919d..00000000 --- a/src/main/webapp/app/entities/customer/customer-delete-dialog.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; - -import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { JhiEventManager } from 'ng-jhipster'; - -import { ICustomer } from 'app/shared/model/customer.model'; -import { CustomerService } from './customer.service'; - -@Component({ - selector: 'jhi-customer-delete-dialog', - templateUrl: './customer-delete-dialog.component.html' -}) -export class CustomerDeleteDialogComponent { - customer: ICustomer; - - constructor(protected customerService: CustomerService, public activeModal: NgbActiveModal, protected eventManager: JhiEventManager) {} - - clear() { - this.activeModal.dismiss('cancel'); - } - - confirmDelete(id: number) { - this.customerService.delete(id).subscribe(response => { - this.eventManager.broadcast({ - name: 'customerListModification', - content: 'Deleted an customer' - }); - this.activeModal.dismiss(true); - }); - } -} - -@Component({ - selector: 'jhi-customer-delete-popup', - template: '' -}) -export class CustomerDeletePopupComponent implements OnInit, OnDestroy { - protected ngbModalRef: NgbModalRef; - - constructor(protected activatedRoute: ActivatedRoute, protected router: Router, protected modalService: NgbModal) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ customer }) => { - setTimeout(() => { - this.ngbModalRef = this.modalService.open(CustomerDeleteDialogComponent as Component, { size: 'lg', backdrop: 'static' }); - this.ngbModalRef.componentInstance.customer = customer; - this.ngbModalRef.result.then( - result => { - this.router.navigate(['/customer', { outlets: { popup: null } }]); - this.ngbModalRef = null; - }, - reason => { - this.router.navigate(['/customer', { outlets: { popup: null } }]); - this.ngbModalRef = null; - } - ); - }, 0); - }); - } - - ngOnDestroy() { - this.ngbModalRef = null; - } -} diff --git a/src/main/webapp/app/entities/customer/customer-detail.component.html b/src/main/webapp/app/entities/customer/customer-detail.component.html deleted file mode 100644 index 5e479fbb..00000000 --- a/src/main/webapp/app/entities/customer/customer-detail.component.html +++ /dev/null @@ -1,83 +0,0 @@ -
-
-
-

Customer: {{customer.displayLabel}}

-
- -
-
Reference
-
- {{customer.reference}} -
-
Prefix
-
- {{customer.prefix}} -
-
Name
-
- {{customer.name}} -
-
Kind
-
- {{customer.kind}} -
-
Birth Date
-
- {{customer.birthDate}} -
-
Birth Place
-
- {{customer.birthPlace}} -
-
Registration Court
-
- {{customer.registrationCourt}} -
-
Registration Number
-
- {{customer.registrationNumber}} -
-
Vat Region
-
- {{customer.vatRegion}} -
-
Vat Number
-
- {{customer.vatNumber}} -
-
Contractual Salutation
-
- {{customer.contractualSalutation}} -
-
Contractual Address
-
- -
-
Billing Salutation
-
- {{customer.billingSalutation}} -
-
Billing Address
-
- -
-
Remark
-
- -
-
- - - - -
-
-
diff --git a/src/main/webapp/app/entities/customer/customer-detail.component.ts b/src/main/webapp/app/entities/customer/customer-detail.component.ts deleted file mode 100644 index da1b94e3..00000000 --- a/src/main/webapp/app/entities/customer/customer-detail.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { ICustomer } from 'app/shared/model/customer.model'; - -@Component({ - selector: 'jhi-customer-detail', - templateUrl: './customer-detail.component.html' -}) -export class CustomerDetailComponent implements OnInit { - customer: ICustomer; - - constructor(protected activatedRoute: ActivatedRoute) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ customer }) => { - this.customer = customer; - }); - } - - previousState() { - window.history.back(); - } -} diff --git a/src/main/webapp/app/entities/customer/customer-update.component.html b/src/main/webapp/app/entities/customer/customer-update.component.html deleted file mode 100644 index c59b92ac..00000000 --- a/src/main/webapp/app/entities/customer/customer-update.component.html +++ /dev/null @@ -1,222 +0,0 @@ -
-
-
-

Create or edit a Customer

-
- -
- - -
-
- - -
- - This field is required. - - - This field should be at least 10000. - - - This field cannot be more than 99999. - - - This field should be a number. - -
-
-
- - -
- - This field is required. - - - This field cannot be longer than 3 characters. - - - This field should follow pattern for "Prefix". - -
-
-
- - -
- - This field is required. - - - This field cannot be longer than 80 characters. - -
-
-
- - -
- - This field is required. - -
-
-
- -
- - - - -
-
-
- - -
- - This field cannot be longer than 80 characters. - -
-
-
- - -
- - This field cannot be longer than 80 characters. - -
-
-
- - -
- - This field cannot be longer than 80 characters. - -
-
-
- - -
- - This field is required. - -
-
-
- - -
- - This field cannot be longer than 40 characters. - -
-
-
- - -
- - This field cannot be longer than 80 characters. - -
-
-
- - -
- - This field is required. - - - This field cannot be longer than 400 characters. - -
-
-
- - -
- - This field cannot be longer than 80 characters. - -
-
-
- - -
- - This field cannot be longer than 400 characters. - -
-
-
- - -
- - This field cannot be longer than 160 characters. - -
-
- -
-
- - -
-
-
-
diff --git a/src/main/webapp/app/entities/customer/customer-update.component.ts b/src/main/webapp/app/entities/customer/customer-update.component.ts deleted file mode 100644 index e6c33ae8..00000000 --- a/src/main/webapp/app/entities/customer/customer-update.component.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { ICustomer } from 'app/shared/model/customer.model'; -import { CustomerService } from './customer.service'; - -@Component({ - selector: 'jhi-customer-update', - templateUrl: './customer-update.component.html' -}) -export class CustomerUpdateComponent implements OnInit { - customer: ICustomer; - isSaving: boolean; - birthDateDp: any; - - constructor(protected customerService: CustomerService, protected activatedRoute: ActivatedRoute) {} - - ngOnInit() { - this.isSaving = false; - this.activatedRoute.data.subscribe(({ customer }) => { - this.customer = customer; - }); - } - - previousState() { - window.history.back(); - } - - save() { - this.isSaving = true; - if (this.customer.id !== undefined) { - this.subscribeToSaveResponse(this.customerService.update(this.customer)); - } else { - this.subscribeToSaveResponse(this.customerService.create(this.customer)); - } - } - - protected subscribeToSaveResponse(result: Observable>) { - result.subscribe((res: HttpResponse) => this.onSaveSuccess(), (res: HttpErrorResponse) => this.onSaveError()); - } - - protected onSaveSuccess() { - this.isSaving = false; - this.previousState(); - } - - protected onSaveError() { - this.isSaving = false; - } -} diff --git a/src/main/webapp/app/entities/customer/customer.component.html b/src/main/webapp/app/entities/customer/customer.component.html deleted file mode 100644 index f3183904..00000000 --- a/src/main/webapp/app/entities/customer/customer.component.html +++ /dev/null @@ -1,74 +0,0 @@ -
-

- Customers - -

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ID Reference Prefix Name Kind
- -
{{customer.id}}{{customer.reference}}{{customer.prefix}}{{customer.name}}{{customer.kind}} -
- - - -
-
-
-
diff --git a/src/main/webapp/app/entities/customer/customer.component.ts b/src/main/webapp/app/entities/customer/customer.component.ts deleted file mode 100644 index 94aa2e18..00000000 --- a/src/main/webapp/app/entities/customer/customer.component.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { Subscription } from 'rxjs'; -import { JhiAlertService, JhiEventManager, JhiParseLinks } from 'ng-jhipster'; - -import { ICustomer } from 'app/shared/model/customer.model'; -import { AccountService } from 'app/core'; - -import { ITEMS_PER_PAGE } from 'app/shared'; -import { CustomerService } from './customer.service'; -import { TableFilter } from 'app/shared/util/tablefilter'; - -@Component({ - selector: 'jhi-customer', - templateUrl: './customer.component.html' -}) -export class CustomerComponent implements OnInit, OnDestroy { - customers: ICustomer[]; - currentAccount: any; - eventSubscriber: Subscription; - itemsPerPage: number; - links: any; - page: any; - predicate: any; - reverse: any; - totalItems: number; - filter: TableFilter<{ reference?: string; prefix?: string; name?: string; kind?: string }>; - - constructor( - protected customerService: CustomerService, - protected jhiAlertService: JhiAlertService, - protected eventManager: JhiEventManager, - protected parseLinks: JhiParseLinks, - protected accountService: AccountService - ) { - this.customers = []; - this.itemsPerPage = ITEMS_PER_PAGE; - this.page = 0; - this.links = { - last: 0 - }; - this.predicate = 'id'; - this.reverse = true; - this.filter = new TableFilter( - { - reference: 'reference.equals', - prefix: 'prefix.contains', - name: 'name.contains', - kind: 'kind.equals' - }, - 500, - () => { - this.reset(); - } - ); - } - - loadAll() { - this.customerService - .query({ - ...this.filter.buildQueryCriteria(), - page: this.page, - size: this.itemsPerPage, - sort: this.sort() - }) - .subscribe( - (res: HttpResponse) => this.paginateCustomers(res.body, res.headers), - (res: HttpErrorResponse) => this.onError(res.message) - ); - } - - reset() { - this.page = 0; - this.customers = []; - this.loadAll(); - } - - loadPage(page) { - this.page = page; - this.loadAll(); - } - - ngOnInit() { - this.loadAll(); - this.accountService.identity().then(account => { - this.currentAccount = account; - }); - this.registerChangeInCustomers(); - } - - ngOnDestroy() { - this.eventManager.destroy(this.eventSubscriber); - } - - trackId(index: number, item: ICustomer) { - return item.id; - } - - registerChangeInCustomers() { - this.eventSubscriber = this.eventManager.subscribe('customerListModification', response => this.reset()); - } - - sort() { - const result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; - if (this.predicate !== 'id') { - result.push('id'); - } - return result; - } - - protected paginateCustomers(data: ICustomer[], headers: HttpHeaders) { - this.links = this.parseLinks.parse(headers.get('link')); - this.totalItems = parseInt(headers.get('X-Total-Count'), 10); - for (let i = 0; i < data.length; i++) { - this.customers.push(data[i]); - } - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } -} diff --git a/src/main/webapp/app/entities/customer/customer.module.ts b/src/main/webapp/app/entities/customer/customer.module.ts deleted file mode 100644 index 8d50348c..00000000 --- a/src/main/webapp/app/entities/customer/customer.module.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { JhiLanguageService } from 'ng-jhipster'; -import { JhiLanguageHelper } from 'app/core'; - -import { HsadminNgSharedModule } from 'app/shared'; -import { - CustomerComponent, - CustomerDetailComponent, - CustomerUpdateComponent, - CustomerDeletePopupComponent, - CustomerDeleteDialogComponent, - customerRoute, - customerPopupRoute -} from './'; - -const ENTITY_STATES = [...customerRoute, ...customerPopupRoute]; - -@NgModule({ - imports: [HsadminNgSharedModule, RouterModule.forChild(ENTITY_STATES)], - declarations: [ - CustomerComponent, - CustomerDetailComponent, - CustomerUpdateComponent, - CustomerDeleteDialogComponent, - CustomerDeletePopupComponent - ], - entryComponents: [CustomerComponent, CustomerUpdateComponent, CustomerDeleteDialogComponent, CustomerDeletePopupComponent], - providers: [{ provide: JhiLanguageService, useClass: JhiLanguageService }], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgCustomerModule { - constructor(private languageService: JhiLanguageService, private languageHelper: JhiLanguageHelper) { - this.languageHelper.language.subscribe((languageKey: string) => { - if (languageKey !== undefined) { - this.languageService.changeLanguage(languageKey); - } - }); - } -} diff --git a/src/main/webapp/app/entities/customer/customer.route.ts b/src/main/webapp/app/entities/customer/customer.route.ts deleted file mode 100644 index 269760db..00000000 --- a/src/main/webapp/app/entities/customer/customer.route.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes } from '@angular/router'; -import { UserRouteAccessService } from 'app/core'; -import { Observable, of } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { Customer } from 'app/shared/model/customer.model'; -import { CustomerService } from './customer.service'; -import { CustomerComponent } from './customer.component'; -import { CustomerDetailComponent } from './customer-detail.component'; -import { CustomerUpdateComponent } from './customer-update.component'; -import { CustomerDeletePopupComponent } from './customer-delete-dialog.component'; -import { ICustomer } from 'app/shared/model/customer.model'; - -@Injectable({ providedIn: 'root' }) -export class CustomerResolve implements Resolve { - constructor(private service: CustomerService) {} - - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - const id = route.params['id'] ? route.params['id'] : null; - if (id) { - return this.service.find(id).pipe( - filter((response: HttpResponse) => response.ok), - map((customer: HttpResponse) => customer.body) - ); - } - return of(new Customer()); - } -} - -export const customerRoute: Routes = [ - { - path: '', - component: CustomerComponent, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.customer.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/view', - component: CustomerDetailComponent, - resolve: { - customer: CustomerResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.customer.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: 'new', - component: CustomerUpdateComponent, - resolve: { - customer: CustomerResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.customer.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/edit', - component: CustomerUpdateComponent, - resolve: { - customer: CustomerResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.customer.home.title' - }, - canActivate: [UserRouteAccessService] - } -]; - -export const customerPopupRoute: Routes = [ - { - path: ':id/delete', - component: CustomerDeletePopupComponent, - resolve: { - customer: CustomerResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.customer.home.title' - }, - canActivate: [UserRouteAccessService], - outlet: 'popup' - } -]; diff --git a/src/main/webapp/app/entities/customer/customer.service.ts b/src/main/webapp/app/entities/customer/customer.service.ts deleted file mode 100644 index 7b6951ce..00000000 --- a/src/main/webapp/app/entities/customer/customer.service.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { map } from 'rxjs/operators'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { createRequestOption } from 'app/shared'; -import { ICustomer } from 'app/shared/model/customer.model'; - -type EntityResponseType = HttpResponse; -type EntityArrayResponseType = HttpResponse; - -@Injectable({ providedIn: 'root' }) -export class CustomerService { - public resourceUrl = SERVER_API_URL + 'api/customers'; - - constructor(protected http: HttpClient) {} - - create(customer: ICustomer): Observable { - const copy = this.convertDateFromClient(customer); - return this.http - .post(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - update(customer: ICustomer): Observable { - const copy = this.convertDateFromClient(customer); - return this.http - .put(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - find(id: number): Observable { - return this.http - .get(`${this.resourceUrl}/${id}`, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - query(req?: any): Observable { - const options = createRequestOption(req); - return this.http - .get(this.resourceUrl, { params: options, observe: 'response' }) - .pipe(map((res: EntityArrayResponseType) => this.convertDateArrayFromServer(res))); - } - - delete(id: number): Observable> { - return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); - } - - protected convertDateFromClient(customer: ICustomer): ICustomer { - const copy: ICustomer = Object.assign({}, customer, { - birthDate: customer.birthDate != null && customer.birthDate.isValid() ? customer.birthDate.format(DATE_FORMAT) : null - }); - return copy; - } - - protected convertDateFromServer(res: EntityResponseType): EntityResponseType { - if (res.body) { - res.body.birthDate = res.body.birthDate != null ? moment(res.body.birthDate) : null; - } - return res; - } - - protected convertDateArrayFromServer(res: EntityArrayResponseType): EntityArrayResponseType { - if (res.body) { - res.body.forEach((customer: ICustomer) => { - customer.birthDate = customer.birthDate != null ? moment(customer.birthDate) : null; - }); - } - return res; - } -} diff --git a/src/main/webapp/app/entities/customer/index.ts b/src/main/webapp/app/entities/customer/index.ts deleted file mode 100644 index 27ae3ca8..00000000 --- a/src/main/webapp/app/entities/customer/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './customer.service'; -export * from './customer-update.component'; -export * from './customer-delete-dialog.component'; -export * from './customer-detail.component'; -export * from './customer.component'; -export * from './customer.route'; diff --git a/src/main/webapp/app/entities/entity.module.ts b/src/main/webapp/app/entities/entity.module.ts deleted file mode 100644 index bdcacf35..00000000 --- a/src/main/webapp/app/entities/entity.module.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -@NgModule({ - imports: [ - RouterModule.forChild([ - { - path: 'customer', - loadChildren: './customer/customer.module#HsadminNgCustomerModule' - }, - { - path: 'membership', - loadChildren: './membership/membership.module#HsadminNgMembershipModule' - }, - { - path: 'share', - loadChildren: './share/share.module#HsadminNgShareModule' - }, - { - path: 'asset', - loadChildren: './asset/asset.module#HsadminNgAssetModule' - }, - { - path: 'sepa-mandate', - loadChildren: './sepa-mandate/sepa-mandate.module#HsadminNgSepaMandateModule' - }, - { - path: 'user-role-assignment', - loadChildren: './user-role-assignment/user-role-assignment.module#HsadminNgUserRoleAssignmentModule' - } - /* jhipster-needle-add-entity-route - JHipster will add entity modules routes here */ - ]) - ], - declarations: [], - entryComponents: [], - providers: [], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgEntityModule {} diff --git a/src/main/webapp/app/entities/membership/index.ts b/src/main/webapp/app/entities/membership/index.ts deleted file mode 100644 index 13a7f938..00000000 --- a/src/main/webapp/app/entities/membership/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './membership.service'; -export * from './membership-update.component'; -export * from './membership-delete-dialog.component'; -export * from './membership-detail.component'; -export * from './membership.component'; -export * from './membership.route'; diff --git a/src/main/webapp/app/entities/membership/membership-delete-dialog.component.html b/src/main/webapp/app/entities/membership/membership-delete-dialog.component.html deleted file mode 100644 index 8749c4ee..00000000 --- a/src/main/webapp/app/entities/membership/membership-delete-dialog.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
- - - -
diff --git a/src/main/webapp/app/entities/membership/membership-delete-dialog.component.ts b/src/main/webapp/app/entities/membership/membership-delete-dialog.component.ts deleted file mode 100644 index ac1787db..00000000 --- a/src/main/webapp/app/entities/membership/membership-delete-dialog.component.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; - -import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { JhiEventManager } from 'ng-jhipster'; - -import { IMembership } from 'app/shared/model/membership.model'; -import { MembershipService } from './membership.service'; - -@Component({ - selector: 'jhi-membership-delete-dialog', - templateUrl: './membership-delete-dialog.component.html' -}) -export class MembershipDeleteDialogComponent { - membership: IMembership; - - constructor( - protected membershipService: MembershipService, - public activeModal: NgbActiveModal, - protected eventManager: JhiEventManager - ) {} - - clear() { - this.activeModal.dismiss('cancel'); - } - - confirmDelete(id: number) { - this.membershipService.delete(id).subscribe(response => { - this.eventManager.broadcast({ - name: 'membershipListModification', - content: 'Deleted an membership' - }); - this.activeModal.dismiss(true); - }); - } -} - -@Component({ - selector: 'jhi-membership-delete-popup', - template: '' -}) -export class MembershipDeletePopupComponent implements OnInit, OnDestroy { - protected ngbModalRef: NgbModalRef; - - constructor(protected activatedRoute: ActivatedRoute, protected router: Router, protected modalService: NgbModal) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ membership }) => { - setTimeout(() => { - this.ngbModalRef = this.modalService.open(MembershipDeleteDialogComponent as Component, { size: 'lg', backdrop: 'static' }); - this.ngbModalRef.componentInstance.membership = membership; - this.ngbModalRef.result.then( - result => { - this.router.navigate(['/membership', { outlets: { popup: null } }]); - this.ngbModalRef = null; - }, - reason => { - this.router.navigate(['/membership', { outlets: { popup: null } }]); - this.ngbModalRef = null; - } - ); - }, 0); - }); - } - - ngOnDestroy() { - this.ngbModalRef = null; - } -} diff --git a/src/main/webapp/app/entities/membership/membership-detail.component.html b/src/main/webapp/app/entities/membership/membership-detail.component.html deleted file mode 100644 index dae64d50..00000000 --- a/src/main/webapp/app/entities/membership/membership-detail.component.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
-
-

Membership: {{membership.displayLabel}}

-
- -
-
Admission Document Date
-
- {{membership.admissionDocumentDate | date:'mediumDate'}} -
-
Cancellation Document Date
-
- {{membership.cancellationDocumentDate | date:'mediumDate'}} -
-
Member From Date
-
- {{membership.memberFromDate | date:'mediumDate'}} -
-
Member Until Date
-
- {{membership.memberUntilDate | date:'mediumDate'}} -
-
Remark
-
- -
-
Customer
-
- -
-
- - - - -
-
-
diff --git a/src/main/webapp/app/entities/membership/membership-detail.component.ts b/src/main/webapp/app/entities/membership/membership-detail.component.ts deleted file mode 100644 index ed1105eb..00000000 --- a/src/main/webapp/app/entities/membership/membership-detail.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { IMembership } from 'app/shared/model/membership.model'; - -@Component({ - selector: 'jhi-membership-detail', - templateUrl: './membership-detail.component.html' -}) -export class MembershipDetailComponent implements OnInit { - membership: IMembership; - - constructor(protected activatedRoute: ActivatedRoute) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ membership }) => { - this.membership = membership; - }); - } - - previousState() { - window.history.back(); - } -} diff --git a/src/main/webapp/app/entities/membership/membership-update.component.html b/src/main/webapp/app/entities/membership/membership-update.component.html deleted file mode 100644 index 758ce53a..00000000 --- a/src/main/webapp/app/entities/membership/membership-update.component.html +++ /dev/null @@ -1,100 +0,0 @@ -
-
-
-

Create or edit a Membership

-
- -
- - -
-
- -
- - - - -
-
- - This field is required. - -
-
-
- -
- - - - -
-
-
- -
- - - - -
-
- - This field is required. - -
-
-
- -
- - - - -
-
-
- - -
- - This field cannot be longer than 160 characters. - -
-
- -
- - -
-
- - This field is required. - -
-
-
- - -
-
-
-
diff --git a/src/main/webapp/app/entities/membership/membership-update.component.ts b/src/main/webapp/app/entities/membership/membership-update.component.ts deleted file mode 100644 index 74140751..00000000 --- a/src/main/webapp/app/entities/membership/membership-update.component.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { JhiAlertService } from 'ng-jhipster'; -import { IMembership } from 'app/shared/model/membership.model'; -import { MembershipService } from './membership.service'; -import { ICustomer } from 'app/shared/model/customer.model'; -import { CustomerService } from 'app/entities/customer'; - -@Component({ - selector: 'jhi-membership-update', - templateUrl: './membership-update.component.html' -}) -export class MembershipUpdateComponent implements OnInit { - membership: IMembership; - isSaving: boolean; - - customers: ICustomer[]; - admissionDocumentDateDp: any; - cancellationDocumentDateDp: any; - memberFromDateDp: any; - memberUntilDateDp: any; - - constructor( - protected jhiAlertService: JhiAlertService, - protected membershipService: MembershipService, - protected customerService: CustomerService, - protected activatedRoute: ActivatedRoute - ) {} - - ngOnInit() { - this.isSaving = false; - this.activatedRoute.data.subscribe(({ membership }) => { - this.membership = membership; - }); - this.customerService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: ICustomer[]) => (this.customers = res), (res: HttpErrorResponse) => this.onError(res.message)); - } - - previousState() { - window.history.back(); - } - - save() { - this.isSaving = true; - if (this.membership.id !== undefined) { - this.subscribeToSaveResponse(this.membershipService.update(this.membership)); - } else { - this.subscribeToSaveResponse(this.membershipService.create(this.membership)); - } - } - - protected subscribeToSaveResponse(result: Observable>) { - result.subscribe((res: HttpResponse) => this.onSaveSuccess(), (res: HttpErrorResponse) => this.onSaveError()); - } - - protected onSaveSuccess() { - this.isSaving = false; - this.previousState(); - } - - protected onSaveError() { - this.isSaving = false; - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } - - trackCustomerById(index: number, item: ICustomer) { - return item.id; - } -} diff --git a/src/main/webapp/app/entities/membership/membership.component.html b/src/main/webapp/app/entities/membership/membership.component.html deleted file mode 100644 index 920fcc61..00000000 --- a/src/main/webapp/app/entities/membership/membership.component.html +++ /dev/null @@ -1,84 +0,0 @@ -
-

- Memberships - -

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ID Admission Document Date Cancellation Document Date Member From Date Member Until Date Customer
- -
{{membership.id}}{{membership.admissionDocumentDate | date:'mediumDate'}}{{membership.cancellationDocumentDate | date:'mediumDate'}}{{membership.memberFromDate | date:'mediumDate'}}{{membership.memberUntilDate | date:'mediumDate'}} - - -
- - - -
-
-
-
diff --git a/src/main/webapp/app/entities/membership/membership.component.ts b/src/main/webapp/app/entities/membership/membership.component.ts deleted file mode 100644 index 5ca7c86a..00000000 --- a/src/main/webapp/app/entities/membership/membership.component.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { Subscription } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { JhiEventManager, JhiParseLinks, JhiAlertService } from 'ng-jhipster'; - -import { IMembership } from 'app/shared/model/membership.model'; -import { AccountService } from 'app/core'; - -import { ITEMS_PER_PAGE } from 'app/shared'; -import { MembershipService } from './membership.service'; -import { ICustomer } from 'app/shared/model/customer.model'; -import { CustomerService } from 'app/entities/customer'; -import { TableFilter, queryYearAsDateRange, queryEquals } from 'app/shared/util/tablefilter'; - -@Component({ - selector: 'jhi-membership', - templateUrl: './membership.component.html' -}) -export class MembershipComponent implements OnInit, OnDestroy { - memberships: IMembership[]; - currentAccount: any; - eventSubscriber: Subscription; - itemsPerPage: number; - links: any; - page: any; - predicate: any; - reverse: any; - totalItems: number; - customers: ICustomer[]; - filter: TableFilter<{ - admissionDocumentDate?: string; - cancellationDocumentDate?: string; - memberFromDate?: string; - memberUntilDate?: string; - customerId?: string; - }>; - - constructor( - protected membershipService: MembershipService, - protected customerService: CustomerService, - protected jhiAlertService: JhiAlertService, - protected eventManager: JhiEventManager, - protected parseLinks: JhiParseLinks, - protected accountService: AccountService - ) { - this.memberships = []; - this.itemsPerPage = ITEMS_PER_PAGE; - this.page = 0; - this.links = { - last: 0 - }; - this.predicate = 'id'; - this.reverse = true; - this.filter = new TableFilter( - { - admissionDocumentDate: queryYearAsDateRange, - cancellationDocumentDate: queryYearAsDateRange, - memberFromDate: queryYearAsDateRange, - memberUntilDate: queryYearAsDateRange, - customerId: queryEquals - }, - 500, - () => { - this.reset(); - } - ); - } - - loadAll() { - this.membershipService - .query({ - ...this.filter.buildQueryCriteria(), - page: this.page, - size: this.itemsPerPage, - sort: this.sort() - }) - .subscribe( - (res: HttpResponse) => this.paginateMemberships(res.body, res.headers), - (res: HttpErrorResponse) => this.onError(res.message) - ); - } - - reset() { - this.page = 0; - this.memberships = []; - this.loadAll(); - } - - loadPage(page) { - this.page = page; - this.loadAll(); - } - - ngOnInit() { - this.loadAll(); - this.accountService.identity().then(account => { - this.currentAccount = account; - }); - this.registerChangeInMemberships(); - this.customerService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: IMembership[]) => (this.customers = res), (res: HttpErrorResponse) => this.onError(res.message)); - } - - ngOnDestroy() { - this.eventManager.destroy(this.eventSubscriber); - } - - trackId(index: number, item: { id: number }) { - return item.id; - } - - registerChangeInMemberships() { - this.eventSubscriber = this.eventManager.subscribe('membershipListModification', response => this.reset()); - } - - sort() { - const result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; - if (this.predicate !== 'id') { - result.push('id'); - } - return result; - } - - protected paginateMemberships(data: IMembership[], headers: HttpHeaders) { - this.links = this.parseLinks.parse(headers.get('link')); - this.totalItems = parseInt(headers.get('X-Total-Count'), 10); - this.memberships = []; - for (let i = 0; i < data.length; i++) { - this.memberships.push(data[i]); - } - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } -} diff --git a/src/main/webapp/app/entities/membership/membership.module.ts b/src/main/webapp/app/entities/membership/membership.module.ts deleted file mode 100644 index 01e0545f..00000000 --- a/src/main/webapp/app/entities/membership/membership.module.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { JhiLanguageService } from 'ng-jhipster'; -import { JhiLanguageHelper } from 'app/core'; - -import { HsadminNgSharedModule } from 'app/shared'; -import { - MembershipComponent, - MembershipDetailComponent, - MembershipUpdateComponent, - MembershipDeletePopupComponent, - MembershipDeleteDialogComponent, - membershipRoute, - membershipPopupRoute -} from './'; - -const ENTITY_STATES = [...membershipRoute, ...membershipPopupRoute]; - -@NgModule({ - imports: [HsadminNgSharedModule, RouterModule.forChild(ENTITY_STATES)], - declarations: [ - MembershipComponent, - MembershipDetailComponent, - MembershipUpdateComponent, - MembershipDeleteDialogComponent, - MembershipDeletePopupComponent - ], - entryComponents: [MembershipComponent, MembershipUpdateComponent, MembershipDeleteDialogComponent, MembershipDeletePopupComponent], - providers: [{ provide: JhiLanguageService, useClass: JhiLanguageService }], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgMembershipModule { - constructor(private languageService: JhiLanguageService, private languageHelper: JhiLanguageHelper) { - this.languageHelper.language.subscribe((languageKey: string) => { - if (languageKey !== undefined) { - this.languageService.changeLanguage(languageKey); - } - }); - } -} diff --git a/src/main/webapp/app/entities/membership/membership.route.ts b/src/main/webapp/app/entities/membership/membership.route.ts deleted file mode 100644 index fbf9f430..00000000 --- a/src/main/webapp/app/entities/membership/membership.route.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes } from '@angular/router'; -import { UserRouteAccessService } from 'app/core'; -import { Observable, of } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { Membership } from 'app/shared/model/membership.model'; -import { MembershipService } from './membership.service'; -import { MembershipComponent } from './membership.component'; -import { MembershipDetailComponent } from './membership-detail.component'; -import { MembershipUpdateComponent } from './membership-update.component'; -import { MembershipDeletePopupComponent } from './membership-delete-dialog.component'; -import { IMembership } from 'app/shared/model/membership.model'; - -@Injectable({ providedIn: 'root' }) -export class MembershipResolve implements Resolve { - constructor(private service: MembershipService) {} - - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - const id = route.params['id'] ? route.params['id'] : null; - if (id) { - return this.service.find(id).pipe( - filter((response: HttpResponse) => response.ok), - map((membership: HttpResponse) => membership.body) - ); - } - return of(new Membership()); - } -} - -export const membershipRoute: Routes = [ - { - path: '', - component: MembershipComponent, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.membership.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/view', - component: MembershipDetailComponent, - resolve: { - membership: MembershipResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.membership.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: 'new', - component: MembershipUpdateComponent, - resolve: { - membership: MembershipResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.membership.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/edit', - component: MembershipUpdateComponent, - resolve: { - membership: MembershipResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.membership.home.title' - }, - canActivate: [UserRouteAccessService] - } -]; - -export const membershipPopupRoute: Routes = [ - { - path: ':id/delete', - component: MembershipDeletePopupComponent, - resolve: { - membership: MembershipResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.membership.home.title' - }, - canActivate: [UserRouteAccessService], - outlet: 'popup' - } -]; diff --git a/src/main/webapp/app/entities/membership/membership.service.ts b/src/main/webapp/app/entities/membership/membership.service.ts deleted file mode 100644 index 586846eb..00000000 --- a/src/main/webapp/app/entities/membership/membership.service.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { map } from 'rxjs/operators'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { createRequestOption } from 'app/shared'; -import { IMembership } from 'app/shared/model/membership.model'; - -type EntityResponseType = HttpResponse; -type EntityArrayResponseType = HttpResponse; - -@Injectable({ providedIn: 'root' }) -export class MembershipService { - public resourceUrl = SERVER_API_URL + 'api/memberships'; - - constructor(protected http: HttpClient) {} - - create(membership: IMembership): Observable { - const copy = this.convertDateFromClient(membership); - return this.http - .post(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - update(membership: IMembership): Observable { - const copy = this.convertDateFromClient(membership); - return this.http - .put(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - find(id: number): Observable { - return this.http - .get(`${this.resourceUrl}/${id}`, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - query(req?: any): Observable { - const options = createRequestOption(req); - return this.http - .get(this.resourceUrl, { params: options, observe: 'response' }) - .pipe(map((res: EntityArrayResponseType) => this.convertDateArrayFromServer(res))); - } - - delete(id: number): Observable> { - return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); - } - - protected convertDateFromClient(membership: IMembership): IMembership { - const copy: IMembership = Object.assign({}, membership, { - admissionDocumentDate: - membership.admissionDocumentDate != null && membership.admissionDocumentDate.isValid() - ? membership.admissionDocumentDate.format(DATE_FORMAT) - : null, - cancellationDocumentDate: - membership.cancellationDocumentDate != null && membership.cancellationDocumentDate.isValid() - ? membership.cancellationDocumentDate.format(DATE_FORMAT) - : null, - memberFromDate: - membership.memberFromDate != null && membership.memberFromDate.isValid() - ? membership.memberFromDate.format(DATE_FORMAT) - : null, - memberUntilDate: - membership.memberUntilDate != null && membership.memberUntilDate.isValid() - ? membership.memberUntilDate.format(DATE_FORMAT) - : null - }); - return copy; - } - - protected convertDateFromServer(res: EntityResponseType): EntityResponseType { - if (res.body) { - res.body.admissionDocumentDate = res.body.admissionDocumentDate != null ? moment(res.body.admissionDocumentDate) : null; - res.body.cancellationDocumentDate = - res.body.cancellationDocumentDate != null ? moment(res.body.cancellationDocumentDate) : null; - res.body.memberFromDate = res.body.memberFromDate != null ? moment(res.body.memberFromDate) : null; - res.body.memberUntilDate = res.body.memberUntilDate != null ? moment(res.body.memberUntilDate) : null; - } - return res; - } - - protected convertDateArrayFromServer(res: EntityArrayResponseType): EntityArrayResponseType { - if (res.body) { - res.body.forEach((membership: IMembership) => { - membership.admissionDocumentDate = - membership.admissionDocumentDate != null ? moment(membership.admissionDocumentDate) : null; - membership.cancellationDocumentDate = - membership.cancellationDocumentDate != null ? moment(membership.cancellationDocumentDate) : null; - membership.memberFromDate = membership.memberFromDate != null ? moment(membership.memberFromDate) : null; - membership.memberUntilDate = membership.memberUntilDate != null ? moment(membership.memberUntilDate) : null; - }); - } - return res; - } -} diff --git a/src/main/webapp/app/entities/sepa-mandate/index.ts b/src/main/webapp/app/entities/sepa-mandate/index.ts deleted file mode 100644 index f99de152..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './sepa-mandate.service'; -export * from './sepa-mandate-update.component'; -export * from './sepa-mandate-delete-dialog.component'; -export * from './sepa-mandate-detail.component'; -export * from './sepa-mandate.component'; -export * from './sepa-mandate.route'; diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.html b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.html deleted file mode 100644 index 12baf54a..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
- - - -
diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.ts b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.ts deleted file mode 100644 index 741e2642..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; - -import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { JhiEventManager } from 'ng-jhipster'; - -import { ISepaMandate } from 'app/shared/model/sepa-mandate.model'; -import { SepaMandateService } from './sepa-mandate.service'; - -@Component({ - selector: 'jhi-sepa-mandate-delete-dialog', - templateUrl: './sepa-mandate-delete-dialog.component.html' -}) -export class SepaMandateDeleteDialogComponent { - sepaMandate: ISepaMandate; - - constructor( - protected sepaMandateService: SepaMandateService, - public activeModal: NgbActiveModal, - protected eventManager: JhiEventManager - ) {} - - clear() { - this.activeModal.dismiss('cancel'); - } - - confirmDelete(id: number) { - this.sepaMandateService.delete(id).subscribe(response => { - this.eventManager.broadcast({ - name: 'sepaMandateListModification', - content: 'Deleted an sepaMandate' - }); - this.activeModal.dismiss(true); - }); - } -} - -@Component({ - selector: 'jhi-sepa-mandate-delete-popup', - template: '' -}) -export class SepaMandateDeletePopupComponent implements OnInit, OnDestroy { - protected ngbModalRef: NgbModalRef; - - constructor(protected activatedRoute: ActivatedRoute, protected router: Router, protected modalService: NgbModal) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ sepaMandate }) => { - setTimeout(() => { - this.ngbModalRef = this.modalService.open(SepaMandateDeleteDialogComponent as Component, { - size: 'lg', - backdrop: 'static' - }); - this.ngbModalRef.componentInstance.sepaMandate = sepaMandate; - this.ngbModalRef.result.then( - result => { - this.router.navigate(['/sepa-mandate', { outlets: { popup: null } }]); - this.ngbModalRef = null; - }, - reason => { - this.router.navigate(['/sepa-mandate', { outlets: { popup: null } }]); - this.ngbModalRef = null; - } - ); - }, 0); - }); - } - - ngOnDestroy() { - this.ngbModalRef = null; - } -} diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-detail.component.html b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-detail.component.html deleted file mode 100644 index b12e9d33..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-detail.component.html +++ /dev/null @@ -1,65 +0,0 @@ -
-
-
-

Sepa Mandate {{sepaMandate.id}}

-
- -
-
Reference
-
- {{sepaMandate.reference}} -
-
Iban
-
- {{sepaMandate.iban}} -
-
Bic
-
- {{sepaMandate.bic}} -
-
Granting Document Date
-
- {{sepaMandate.grantingDocumentDate | date:'mediumDate'}} -
-
Revokation Document Date
-
- {{sepaMandate.revokationDocumentDate | date:'mediumDate'}} -
-
Valid From Date
-
- {{sepaMandate.validFromDate | date:'mediumDate'}} -
-
Valid Until Date
-
- {{sepaMandate.validUntilDate | date:'mediumDate'}} -
-
Last Used Date
-
- {{sepaMandate.lastUsedDate | date:'mediumDate'}} -
-
Remark
-
- -
-
Customer
-
- -
-
- - - - -
-
-
diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-detail.component.ts b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-detail.component.ts deleted file mode 100644 index 41c98730..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-detail.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { ISepaMandate } from 'app/shared/model/sepa-mandate.model'; - -@Component({ - selector: 'jhi-sepa-mandate-detail', - templateUrl: './sepa-mandate-detail.component.html' -}) -export class SepaMandateDetailComponent implements OnInit { - sepaMandate: ISepaMandate; - - constructor(protected activatedRoute: ActivatedRoute) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ sepaMandate }) => { - this.sepaMandate = sepaMandate; - }); - } - - previousState() { - window.history.back(); - } -} diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-update.component.html b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-update.component.html deleted file mode 100644 index 50826fd8..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-update.component.html +++ /dev/null @@ -1,147 +0,0 @@ -
-
-
-

Create or edit a Sepa Mandate

-
- -
- - -
-
- - -
- - This field is required. - - - This field cannot be longer than 40 characters. - -
-
-
- - -
- - This field cannot be longer than 34 characters. - -
-
-
- - -
- - This field cannot be longer than 11 characters. - -
-
-
- -
- - - - -
-
- - This field is required. - -
-
-
- -
- - - - -
-
-
- -
- - - - -
-
- - This field is required. - -
-
-
- -
- - - - -
-
-
- -
- - - - -
-
-
- - -
- - This field cannot be longer than 160 characters. - -
-
- -
- - -
-
- - This field is required. - -
-
-
- - -
-
-
-
diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-update.component.ts b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-update.component.ts deleted file mode 100644 index 8efa2127..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate-update.component.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { JhiAlertService } from 'ng-jhipster'; -import { ISepaMandate } from 'app/shared/model/sepa-mandate.model'; -import { SepaMandateService } from './sepa-mandate.service'; -import { ICustomer } from 'app/shared/model/customer.model'; -import { CustomerService } from 'app/entities/customer'; - -@Component({ - selector: 'jhi-sepa-mandate-update', - templateUrl: './sepa-mandate-update.component.html' -}) -export class SepaMandateUpdateComponent implements OnInit { - sepaMandate: ISepaMandate; - isSaving: boolean; - - customers: ICustomer[]; - grantingDocumentDateDp: any; - revokationDocumentDateDp: any; - validFromDateDp: any; - validUntilDateDp: any; - lastUsedDateDp: any; - - constructor( - protected jhiAlertService: JhiAlertService, - protected sepaMandateService: SepaMandateService, - protected customerService: CustomerService, - protected activatedRoute: ActivatedRoute - ) {} - - ngOnInit() { - this.isSaving = false; - this.activatedRoute.data.subscribe(({ sepaMandate }) => { - this.sepaMandate = sepaMandate; - }); - this.customerService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: ICustomer[]) => (this.customers = res), (res: HttpErrorResponse) => this.onError(res.message)); - } - - previousState() { - window.history.back(); - } - - save() { - this.isSaving = true; - if (this.sepaMandate.id !== undefined) { - this.subscribeToSaveResponse(this.sepaMandateService.update(this.sepaMandate)); - } else { - this.subscribeToSaveResponse(this.sepaMandateService.create(this.sepaMandate)); - } - } - - protected subscribeToSaveResponse(result: Observable>) { - result.subscribe((res: HttpResponse) => this.onSaveSuccess(), (res: HttpErrorResponse) => this.onSaveError()); - } - - protected onSaveSuccess() { - this.isSaving = false; - this.previousState(); - } - - protected onSaveError() { - this.isSaving = false; - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } - - trackCustomerById(index: number, item: ICustomer) { - return item.id; - } -} diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.component.html b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.component.html deleted file mode 100644 index cc9c010f..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.component.html +++ /dev/null @@ -1,96 +0,0 @@ -
-

- Sepa Mandates - -

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ID Reference Iban Bic Granting Document Date Revokation Document Date Valid From Date Valid Until Date Last Used Date Customer
- -
{{sepaMandate.id}}{{sepaMandate.reference}}{{sepaMandate.iban}}{{sepaMandate.bic}}{{sepaMandate.grantingDocumentDate | date:'mediumDate'}}{{sepaMandate.revokationDocumentDate | date:'mediumDate'}}{{sepaMandate.validFromDate | date:'mediumDate'}}{{sepaMandate.validUntilDate | date:'mediumDate'}}{{sepaMandate.lastUsedDate | date:'mediumDate'}} - - -
- - - -
-
-
-
diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.component.ts b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.component.ts deleted file mode 100644 index d78686dd..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.component.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { Subscription } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { JhiEventManager, JhiParseLinks, JhiAlertService } from 'ng-jhipster'; - -import { ISepaMandate } from 'app/shared/model/sepa-mandate.model'; -import { AccountService } from 'app/core'; - -import { ITEMS_PER_PAGE } from 'app/shared'; -import { SepaMandateService } from './sepa-mandate.service'; -import { ICustomer } from 'app/shared/model/customer.model'; -import { CustomerService } from 'app/entities/customer'; -import { TableFilter, queryYearAsDateRange, queryEquals, queryContains } from 'app/shared/util/tablefilter'; -import { IMembership } from 'app/shared/model/membership.model'; - -@Component({ - selector: 'jhi-sepa-mandate', - templateUrl: './sepa-mandate.component.html' -}) -export class SepaMandateComponent implements OnInit, OnDestroy { - sepaMandates: ISepaMandate[]; - currentAccount: any; - eventSubscriber: Subscription; - itemsPerPage: number; - links: any; - page: any; - predicate: any; - reverse: any; - totalItems: number; - customers: ICustomer[]; - filter: TableFilter<{ - reference?: string; - iban?: string; - bic?: string; - grantingDocumentDate?: string; - revokationDocumentDate?: string; - validFromDate?: string; - validUntilDate?: string; - lastUsedDate?: string; - customerId?: string; - }>; - - constructor( - protected sepaMandateService: SepaMandateService, - protected customerService: CustomerService, - protected jhiAlertService: JhiAlertService, - protected eventManager: JhiEventManager, - protected parseLinks: JhiParseLinks, - protected accountService: AccountService - ) { - this.sepaMandates = []; - this.itemsPerPage = ITEMS_PER_PAGE; - this.page = 0; - this.links = { - last: 0 - }; - this.predicate = 'id'; - this.reverse = true; - this.filter = new TableFilter( - { - reference: queryContains, - iban: queryContains, - bic: queryContains, - grantingDocumentDate: queryYearAsDateRange, - revokationDocumentDate: queryYearAsDateRange, - validFromDate: queryYearAsDateRange, - validUntilDate: queryYearAsDateRange, - lastUsedDate: queryYearAsDateRange, - customerId: queryEquals - }, - 500, - () => { - this.reset(); - } - ); - } - - loadAll() { - this.sepaMandateService - .query({ - ...this.filter.buildQueryCriteria(), - page: this.page, - size: this.itemsPerPage, - sort: this.sort() - }) - .subscribe( - (res: HttpResponse) => this.paginateSepaMandates(res.body, res.headers), - (res: HttpErrorResponse) => this.onError(res.message) - ); - } - - reset() { - this.page = 0; - this.sepaMandates = []; - this.loadAll(); - } - - loadPage(page) { - this.page = page; - this.loadAll(); - } - - ngOnInit() { - this.loadAll(); - this.accountService.identity().then(account => { - this.currentAccount = account; - }); - this.registerChangeInSepaMandates(); - this.customerService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: IMembership[]) => (this.customers = res), (res: HttpErrorResponse) => this.onError(res.message)); - } - - ngOnDestroy() { - this.eventManager.destroy(this.eventSubscriber); - } - - trackId(index: number, item: { id: number }) { - return item.id; - } - - registerChangeInSepaMandates() { - this.eventSubscriber = this.eventManager.subscribe('sepaMandateListModification', response => this.reset()); - } - - sort() { - const result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; - if (this.predicate !== 'id') { - result.push('id'); - } - return result; - } - - protected paginateSepaMandates(data: ISepaMandate[], headers: HttpHeaders) { - this.links = this.parseLinks.parse(headers.get('link')); - this.totalItems = parseInt(headers.get('X-Total-Count'), 10); - for (let i = 0; i < data.length; i++) { - this.sepaMandates.push(data[i]); - } - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } -} diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.module.ts b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.module.ts deleted file mode 100644 index f94a5113..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.module.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { JhiLanguageService } from 'ng-jhipster'; -import { JhiLanguageHelper } from 'app/core'; - -import { HsadminNgSharedModule } from 'app/shared'; -import { - SepaMandateComponent, - SepaMandateDetailComponent, - SepaMandateUpdateComponent, - SepaMandateDeletePopupComponent, - SepaMandateDeleteDialogComponent, - sepaMandateRoute, - sepaMandatePopupRoute -} from './'; - -const ENTITY_STATES = [...sepaMandateRoute, ...sepaMandatePopupRoute]; - -@NgModule({ - imports: [HsadminNgSharedModule, RouterModule.forChild(ENTITY_STATES)], - declarations: [ - SepaMandateComponent, - SepaMandateDetailComponent, - SepaMandateUpdateComponent, - SepaMandateDeleteDialogComponent, - SepaMandateDeletePopupComponent - ], - entryComponents: [SepaMandateComponent, SepaMandateUpdateComponent, SepaMandateDeleteDialogComponent, SepaMandateDeletePopupComponent], - providers: [{ provide: JhiLanguageService, useClass: JhiLanguageService }], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgSepaMandateModule { - constructor(private languageService: JhiLanguageService, private languageHelper: JhiLanguageHelper) { - this.languageHelper.language.subscribe((languageKey: string) => { - if (languageKey !== undefined) { - this.languageService.changeLanguage(languageKey); - } - }); - } -} diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.route.ts b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.route.ts deleted file mode 100644 index de46aaf3..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.route.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes } from '@angular/router'; -import { UserRouteAccessService } from 'app/core'; -import { Observable, of } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { SepaMandate } from 'app/shared/model/sepa-mandate.model'; -import { SepaMandateService } from './sepa-mandate.service'; -import { SepaMandateComponent } from './sepa-mandate.component'; -import { SepaMandateDetailComponent } from './sepa-mandate-detail.component'; -import { SepaMandateUpdateComponent } from './sepa-mandate-update.component'; -import { SepaMandateDeletePopupComponent } from './sepa-mandate-delete-dialog.component'; -import { ISepaMandate } from 'app/shared/model/sepa-mandate.model'; - -@Injectable({ providedIn: 'root' }) -export class SepaMandateResolve implements Resolve { - constructor(private service: SepaMandateService) {} - - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - const id = route.params['id'] ? route.params['id'] : null; - if (id) { - return this.service.find(id).pipe( - filter((response: HttpResponse) => response.ok), - map((sepaMandate: HttpResponse) => sepaMandate.body) - ); - } - return of(new SepaMandate()); - } -} - -export const sepaMandateRoute: Routes = [ - { - path: '', - component: SepaMandateComponent, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.sepaMandate.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/view', - component: SepaMandateDetailComponent, - resolve: { - sepaMandate: SepaMandateResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.sepaMandate.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: 'new', - component: SepaMandateUpdateComponent, - resolve: { - sepaMandate: SepaMandateResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.sepaMandate.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/edit', - component: SepaMandateUpdateComponent, - resolve: { - sepaMandate: SepaMandateResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.sepaMandate.home.title' - }, - canActivate: [UserRouteAccessService] - } -]; - -export const sepaMandatePopupRoute: Routes = [ - { - path: ':id/delete', - component: SepaMandateDeletePopupComponent, - resolve: { - sepaMandate: SepaMandateResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.sepaMandate.home.title' - }, - canActivate: [UserRouteAccessService], - outlet: 'popup' - } -]; diff --git a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.service.ts b/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.service.ts deleted file mode 100644 index 0cf01c0a..00000000 --- a/src/main/webapp/app/entities/sepa-mandate/sepa-mandate.service.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { map } from 'rxjs/operators'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { createRequestOption } from 'app/shared'; -import { ISepaMandate } from 'app/shared/model/sepa-mandate.model'; - -type EntityResponseType = HttpResponse; -type EntityArrayResponseType = HttpResponse; - -@Injectable({ providedIn: 'root' }) -export class SepaMandateService { - public resourceUrl = SERVER_API_URL + 'api/sepa-mandates'; - - constructor(protected http: HttpClient) {} - - create(sepaMandate: ISepaMandate): Observable { - const copy = this.convertDateFromClient(sepaMandate); - return this.http - .post(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - update(sepaMandate: ISepaMandate): Observable { - const copy = this.convertDateFromClient(sepaMandate); - return this.http - .put(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - find(id: number): Observable { - return this.http - .get(`${this.resourceUrl}/${id}`, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - query(req?: any): Observable { - const options = createRequestOption(req); - return this.http - .get(this.resourceUrl, { params: options, observe: 'response' }) - .pipe(map((res: EntityArrayResponseType) => this.convertDateArrayFromServer(res))); - } - - delete(id: number): Observable> { - return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); - } - - protected convertDateFromClient(sepaMandate: ISepaMandate): ISepaMandate { - const copy: ISepaMandate = Object.assign({}, sepaMandate, { - grantingDocumentDate: - sepaMandate.grantingDocumentDate != null && sepaMandate.grantingDocumentDate.isValid() - ? sepaMandate.grantingDocumentDate.format(DATE_FORMAT) - : null, - revokationDocumentDate: - sepaMandate.revokationDocumentDate != null && sepaMandate.revokationDocumentDate.isValid() - ? sepaMandate.revokationDocumentDate.format(DATE_FORMAT) - : null, - validFromDate: - sepaMandate.validFromDate != null && sepaMandate.validFromDate.isValid() - ? sepaMandate.validFromDate.format(DATE_FORMAT) - : null, - validUntilDate: - sepaMandate.validUntilDate != null && sepaMandate.validUntilDate.isValid() - ? sepaMandate.validUntilDate.format(DATE_FORMAT) - : null, - lastUsedDate: - sepaMandate.lastUsedDate != null && sepaMandate.lastUsedDate.isValid() ? sepaMandate.lastUsedDate.format(DATE_FORMAT) : null - }); - return copy; - } - - protected convertDateFromServer(res: EntityResponseType): EntityResponseType { - if (res.body) { - res.body.grantingDocumentDate = res.body.grantingDocumentDate != null ? moment(res.body.grantingDocumentDate) : null; - res.body.revokationDocumentDate = res.body.revokationDocumentDate != null ? moment(res.body.revokationDocumentDate) : null; - res.body.validFromDate = res.body.validFromDate != null ? moment(res.body.validFromDate) : null; - res.body.validUntilDate = res.body.validUntilDate != null ? moment(res.body.validUntilDate) : null; - res.body.lastUsedDate = res.body.lastUsedDate != null ? moment(res.body.lastUsedDate) : null; - } - return res; - } - - protected convertDateArrayFromServer(res: EntityArrayResponseType): EntityArrayResponseType { - if (res.body) { - res.body.forEach((sepaMandate: ISepaMandate) => { - sepaMandate.grantingDocumentDate = - sepaMandate.grantingDocumentDate != null ? moment(sepaMandate.grantingDocumentDate) : null; - sepaMandate.revokationDocumentDate = - sepaMandate.revokationDocumentDate != null ? moment(sepaMandate.revokationDocumentDate) : null; - sepaMandate.validFromDate = sepaMandate.validFromDate != null ? moment(sepaMandate.validFromDate) : null; - sepaMandate.validUntilDate = sepaMandate.validUntilDate != null ? moment(sepaMandate.validUntilDate) : null; - sepaMandate.lastUsedDate = sepaMandate.lastUsedDate != null ? moment(sepaMandate.lastUsedDate) : null; - }); - } - return res; - } -} diff --git a/src/main/webapp/app/entities/share/index.ts b/src/main/webapp/app/entities/share/index.ts deleted file mode 100644 index ecc5eeaa..00000000 --- a/src/main/webapp/app/entities/share/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './share.service'; -export * from './share-update.component'; -export * from './share-delete-dialog.component'; -export * from './share-detail.component'; -export * from './share.component'; -export * from './share.route'; diff --git a/src/main/webapp/app/entities/share/share-delete-dialog.component.html b/src/main/webapp/app/entities/share/share-delete-dialog.component.html deleted file mode 100644 index 5437602b..00000000 --- a/src/main/webapp/app/entities/share/share-delete-dialog.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
- - - -
diff --git a/src/main/webapp/app/entities/share/share-delete-dialog.component.ts b/src/main/webapp/app/entities/share/share-delete-dialog.component.ts deleted file mode 100644 index 34138baa..00000000 --- a/src/main/webapp/app/entities/share/share-delete-dialog.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; - -import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { JhiEventManager } from 'ng-jhipster'; - -import { IShare } from 'app/shared/model/share.model'; -import { ShareService } from './share.service'; - -@Component({ - selector: 'jhi-share-delete-dialog', - templateUrl: './share-delete-dialog.component.html' -}) -export class ShareDeleteDialogComponent { - share: IShare; - - constructor(protected shareService: ShareService, public activeModal: NgbActiveModal, protected eventManager: JhiEventManager) {} - - clear() { - this.activeModal.dismiss('cancel'); - } - - confirmDelete(id: number) { - this.shareService.delete(id).subscribe(response => { - this.eventManager.broadcast({ - name: 'shareListModification', - content: 'Deleted an share' - }); - this.activeModal.dismiss(true); - }); - } -} - -@Component({ - selector: 'jhi-share-delete-popup', - template: '' -}) -export class ShareDeletePopupComponent implements OnInit, OnDestroy { - protected ngbModalRef: NgbModalRef; - - constructor(protected activatedRoute: ActivatedRoute, protected router: Router, protected modalService: NgbModal) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ share }) => { - setTimeout(() => { - this.ngbModalRef = this.modalService.open(ShareDeleteDialogComponent as Component, { size: 'lg', backdrop: 'static' }); - this.ngbModalRef.componentInstance.share = share; - this.ngbModalRef.result.then( - result => { - this.router.navigate(['/share', { outlets: { popup: null } }]); - this.ngbModalRef = null; - }, - reason => { - this.router.navigate(['/share', { outlets: { popup: null } }]); - this.ngbModalRef = null; - } - ); - }, 0); - }); - } - - ngOnDestroy() { - this.ngbModalRef = null; - } -} diff --git a/src/main/webapp/app/entities/share/share-detail.component.html b/src/main/webapp/app/entities/share/share-detail.component.html deleted file mode 100644 index 5dd34103..00000000 --- a/src/main/webapp/app/entities/share/share-detail.component.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
-
-

Share: #{{share.id}} - {{share.membershipDisplayLabel}}

-
- -
-
Document Date
-
- {{share.documentDate | date:'mediumDate'}} -
-
Value Date
-
- {{share.valueDate | date:'mediumDate'}} -
-
Action
-
- {{share.action}} -
-
Quantity
-
- {{share.quantity}} -
-
Remark
-
- -
-
Membership
-
- -
-
- - - - -
-
-
diff --git a/src/main/webapp/app/entities/share/share-detail.component.ts b/src/main/webapp/app/entities/share/share-detail.component.ts deleted file mode 100644 index 5cd2ed0f..00000000 --- a/src/main/webapp/app/entities/share/share-detail.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { IShare } from 'app/shared/model/share.model'; - -@Component({ - selector: 'jhi-share-detail', - templateUrl: './share-detail.component.html' -}) -export class ShareDetailComponent implements OnInit { - share: IShare; - - constructor(protected activatedRoute: ActivatedRoute) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ share }) => { - this.share = share; - }); - } - - previousState() { - window.history.back(); - } -} diff --git a/src/main/webapp/app/entities/share/share-update.component.html b/src/main/webapp/app/entities/share/share-update.component.html deleted file mode 100644 index 78a04f63..00000000 --- a/src/main/webapp/app/entities/share/share-update.component.html +++ /dev/null @@ -1,108 +0,0 @@ -
-
-
-

Create or edit a Share

-
- -
- - -
-
- -
- - - - -
-
- - This field is required. - -
-
-
- -
- - - - -
-
- - This field is required. - -
-
-
- - -
- - This field is required. - -
-
-
- - -
- - This field is required. - - - This field should be a number. - -
-
-
- - -
- - This field cannot be longer than 160 characters. - -
-
- -
- - -
-
- - This field is required. - -
-
-
- - -
-
-
-
diff --git a/src/main/webapp/app/entities/share/share-update.component.ts b/src/main/webapp/app/entities/share/share-update.component.ts deleted file mode 100644 index b031a238..00000000 --- a/src/main/webapp/app/entities/share/share-update.component.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { HttpResponse, HttpErrorResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { JhiAlertService } from 'ng-jhipster'; -import { IShare } from 'app/shared/model/share.model'; -import { ShareService } from './share.service'; -import { IMembership } from 'app/shared/model/membership.model'; -import { MembershipService } from 'app/entities/membership'; - -@Component({ - selector: 'jhi-share-update', - templateUrl: './share-update.component.html' -}) -export class ShareUpdateComponent implements OnInit { - share: IShare; - isSaving: boolean; - - memberships: IMembership[]; - documentDateDp: any; - valueDateDp: any; - - constructor( - protected jhiAlertService: JhiAlertService, - protected shareService: ShareService, - protected membershipService: MembershipService, - protected activatedRoute: ActivatedRoute - ) {} - - ngOnInit() { - this.isSaving = false; - this.activatedRoute.data.subscribe(({ share }) => { - this.share = share; - }); - this.membershipService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: IMembership[]) => (this.memberships = res), (res: HttpErrorResponse) => this.onError(res.message)); - } - - previousState() { - window.history.back(); - } - - save() { - this.isSaving = true; - if (this.share.id !== undefined) { - this.subscribeToSaveResponse(this.shareService.update(this.share)); - } else { - this.subscribeToSaveResponse(this.shareService.create(this.share)); - } - } - - protected subscribeToSaveResponse(result: Observable>) { - result.subscribe((res: HttpResponse) => this.onSaveSuccess(), (res: HttpErrorResponse) => this.onSaveError()); - } - - protected onSaveSuccess() { - this.isSaving = false; - this.previousState(); - } - - protected onSaveError() { - this.isSaving = false; - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } - - trackMembershipById(index: number, item: IMembership) { - return item.id; - } -} diff --git a/src/main/webapp/app/entities/share/share.component.html b/src/main/webapp/app/entities/share/share.component.html deleted file mode 100644 index 7ac7b10b..00000000 --- a/src/main/webapp/app/entities/share/share.component.html +++ /dev/null @@ -1,90 +0,0 @@ -
-

- Shares - -

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ID Document Date Value Date Action Quantity Membership
- - - -
{{share.id}}{{share.documentDate | date:'mediumDate'}}{{share.valueDate | date:'mediumDate'}}{{share.action}}{{share.quantity}} - - -
- - - -
-
-
-
diff --git a/src/main/webapp/app/entities/share/share.component.ts b/src/main/webapp/app/entities/share/share.component.ts deleted file mode 100644 index 831b30c6..00000000 --- a/src/main/webapp/app/entities/share/share.component.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { Subscription } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { JhiEventManager, JhiParseLinks, JhiAlertService } from 'ng-jhipster'; - -import { IShare } from 'app/shared/model/share.model'; -import { AccountService } from 'app/core'; - -import { ITEMS_PER_PAGE } from 'app/shared'; -import { ShareService } from './share.service'; -import { IMembership } from 'app/shared/model/membership.model'; -import { MembershipService } from 'app/entities/membership'; -import { TableFilter, queryYearAsDateRange, queryEquals } from 'app/shared/util/tablefilter'; - -@Component({ - selector: 'jhi-share', - templateUrl: './share.component.html' -}) -export class ShareComponent implements OnInit, OnDestroy { - shares: IShare[]; - currentAccount: any; - eventSubscriber: Subscription; - itemsPerPage: number; - links: any; - page: any; - predicate: any; - reverse: any; - totalItems: number; - memberships: IMembership[]; - filter: TableFilter<{ - documentDate?: string; - valueDate?: string; - action?: string; - quantity?: string; - membershipId?: string; - }>; - - constructor( - protected shareService: ShareService, - protected membershipService: MembershipService, - protected jhiAlertService: JhiAlertService, - protected eventManager: JhiEventManager, - protected parseLinks: JhiParseLinks, - protected accountService: AccountService - ) { - this.shares = []; - this.itemsPerPage = ITEMS_PER_PAGE; - this.page = 0; - this.links = { - last: 0 - }; - this.predicate = 'id'; - this.reverse = true; - this.filter = new TableFilter( - { - documentDate: queryYearAsDateRange, - valueDate: queryYearAsDateRange, - action: queryEquals, - quantity: queryEquals, - membershipId: queryEquals - }, - 500, - () => { - this.reset(); - } - ); - } - - loadAll() { - this.shareService - .query({ - ...this.filter.buildQueryCriteria(), - page: this.page, - size: this.itemsPerPage, - sort: this.sort() - }) - .subscribe( - (res: HttpResponse) => this.paginateShares(res.body, res.headers), - (res: HttpErrorResponse) => this.onError(res.message) - ); - } - - reset() { - this.page = 0; - this.shares = []; - this.loadAll(); - } - - loadPage(page) { - this.page = page; - this.loadAll(); - } - - ngOnInit() { - this.loadAll(); - this.accountService.identity().then(account => { - this.currentAccount = account; - }); - this.membershipService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: IMembership[]) => (this.memberships = res), (res: HttpErrorResponse) => this.onError(res.message)); - this.registerChangeInShares(); - } - - ngOnDestroy() { - this.eventManager.destroy(this.eventSubscriber); - } - - trackId(index: number, item: { id: number }) { - return item.id; - } - - registerChangeInShares() { - this.eventSubscriber = this.eventManager.subscribe('shareListModification', response => this.reset()); - } - - sort() { - const result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; - if (this.predicate !== 'id') { - result.push('id'); - } - return result; - } - - protected paginateShares(data: IShare[], headers: HttpHeaders) { - this.links = this.parseLinks.parse(headers.get('link')); - this.totalItems = parseInt(headers.get('X-Total-Count'), 10); - for (let i = 0; i < data.length; i++) { - this.shares.push(data[i]); - } - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } -} diff --git a/src/main/webapp/app/entities/share/share.module.ts b/src/main/webapp/app/entities/share/share.module.ts deleted file mode 100644 index 9fb07b3a..00000000 --- a/src/main/webapp/app/entities/share/share.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { JhiLanguageService } from 'ng-jhipster'; -import { JhiLanguageHelper } from 'app/core'; - -import { HsadminNgSharedModule } from 'app/shared'; -import { - ShareComponent, - ShareDetailComponent, - ShareUpdateComponent, - ShareDeletePopupComponent, - ShareDeleteDialogComponent, - shareRoute, - sharePopupRoute -} from './'; - -const ENTITY_STATES = [...shareRoute, ...sharePopupRoute]; - -@NgModule({ - imports: [HsadminNgSharedModule, RouterModule.forChild(ENTITY_STATES)], - declarations: [ShareComponent, ShareDetailComponent, ShareUpdateComponent, ShareDeleteDialogComponent, ShareDeletePopupComponent], - entryComponents: [ShareComponent, ShareUpdateComponent, ShareDeleteDialogComponent, ShareDeletePopupComponent], - providers: [{ provide: JhiLanguageService, useClass: JhiLanguageService }], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgShareModule { - constructor(private languageService: JhiLanguageService, private languageHelper: JhiLanguageHelper) { - this.languageHelper.language.subscribe((languageKey: string) => { - if (languageKey !== undefined) { - this.languageService.changeLanguage(languageKey); - } - }); - } -} diff --git a/src/main/webapp/app/entities/share/share.route.ts b/src/main/webapp/app/entities/share/share.route.ts deleted file mode 100644 index 36f35a5b..00000000 --- a/src/main/webapp/app/entities/share/share.route.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes } from '@angular/router'; -import { UserRouteAccessService } from 'app/core'; -import { Observable, of } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { Share } from 'app/shared/model/share.model'; -import { ShareService } from './share.service'; -import { ShareComponent } from './share.component'; -import { ShareDetailComponent } from './share-detail.component'; -import { ShareUpdateComponent } from './share-update.component'; -import { ShareDeletePopupComponent } from './share-delete-dialog.component'; -import { IShare } from 'app/shared/model/share.model'; - -@Injectable({ providedIn: 'root' }) -export class ShareResolve implements Resolve { - constructor(private service: ShareService) {} - - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - const id = route.params['id'] ? route.params['id'] : null; - if (id) { - return this.service.find(id).pipe( - filter((response: HttpResponse) => response.ok), - map((share: HttpResponse) => share.body) - ); - } - return of(new Share()); - } -} - -export const shareRoute: Routes = [ - { - path: '', - component: ShareComponent, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.share.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/view', - component: ShareDetailComponent, - resolve: { - share: ShareResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.share.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: 'new', - component: ShareUpdateComponent, - resolve: { - share: ShareResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.share.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/edit', - component: ShareUpdateComponent, - resolve: { - share: ShareResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.share.home.title' - }, - canActivate: [UserRouteAccessService] - } -]; - -export const sharePopupRoute: Routes = [ - { - path: ':id/delete', - component: ShareDeletePopupComponent, - resolve: { - share: ShareResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.share.home.title' - }, - canActivate: [UserRouteAccessService], - outlet: 'popup' - } -]; diff --git a/src/main/webapp/app/entities/share/share.service.ts b/src/main/webapp/app/entities/share/share.service.ts deleted file mode 100644 index d9bf57ec..00000000 --- a/src/main/webapp/app/entities/share/share.service.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { map } from 'rxjs/operators'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { createRequestOption } from 'app/shared'; -import { IShare } from 'app/shared/model/share.model'; - -type EntityResponseType = HttpResponse; -type EntityArrayResponseType = HttpResponse; - -@Injectable({ providedIn: 'root' }) -export class ShareService { - public resourceUrl = SERVER_API_URL + 'api/shares'; - - constructor(protected http: HttpClient) {} - - create(share: IShare): Observable { - const copy = this.convertDateFromClient(share); - return this.http - .post(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - update(share: IShare): Observable { - const copy = this.convertDateFromClient(share); - return this.http - .put(this.resourceUrl, copy, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - find(id: number): Observable { - return this.http - .get(`${this.resourceUrl}/${id}`, { observe: 'response' }) - .pipe(map((res: EntityResponseType) => this.convertDateFromServer(res))); - } - - query(req?: any): Observable { - const options = createRequestOption(req); - return this.http - .get(this.resourceUrl, { params: options, observe: 'response' }) - .pipe(map((res: EntityArrayResponseType) => this.convertDateArrayFromServer(res))); - } - - delete(id: number): Observable> { - return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); - } - - protected convertDateFromClient(share: IShare): IShare { - const copy: IShare = Object.assign({}, share, { - documentDate: share.documentDate != null && share.documentDate.isValid() ? share.documentDate.format(DATE_FORMAT) : null, - valueDate: share.valueDate != null && share.valueDate.isValid() ? share.valueDate.format(DATE_FORMAT) : null - }); - return copy; - } - - protected convertDateFromServer(res: EntityResponseType): EntityResponseType { - if (res.body) { - res.body.documentDate = res.body.documentDate != null ? moment(res.body.documentDate) : null; - res.body.valueDate = res.body.valueDate != null ? moment(res.body.valueDate) : null; - } - return res; - } - - protected convertDateArrayFromServer(res: EntityArrayResponseType): EntityArrayResponseType { - if (res.body) { - res.body.forEach((share: IShare) => { - share.documentDate = share.documentDate != null ? moment(share.documentDate) : null; - share.valueDate = share.valueDate != null ? moment(share.valueDate) : null; - }); - } - return res; - } -} diff --git a/src/main/webapp/app/entities/user-role-assignment/index.ts b/src/main/webapp/app/entities/user-role-assignment/index.ts deleted file mode 100644 index c09ebecf..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './user-role-assignment.service'; -export * from './user-role-assignment-update.component'; -export * from './user-role-assignment-delete-dialog.component'; -export * from './user-role-assignment-detail.component'; -export * from './user-role-assignment.component'; -export * from './user-role-assignment.route'; diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.html b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.html deleted file mode 100644 index abf0a02e..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
- - - -
diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.ts b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.ts deleted file mode 100644 index 886df8fc..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; - -import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { JhiEventManager } from 'ng-jhipster'; - -import { IUserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; -import { UserRoleAssignmentService } from './user-role-assignment.service'; - -@Component({ - selector: 'jhi-user-role-assignment-delete-dialog', - templateUrl: './user-role-assignment-delete-dialog.component.html' -}) -export class UserRoleAssignmentDeleteDialogComponent { - userRoleAssignment: IUserRoleAssignment; - - constructor( - protected userRoleAssignmentService: UserRoleAssignmentService, - public activeModal: NgbActiveModal, - protected eventManager: JhiEventManager - ) {} - - clear() { - this.activeModal.dismiss('cancel'); - } - - confirmDelete(id: number) { - this.userRoleAssignmentService.delete(id).subscribe(response => { - this.eventManager.broadcast({ - name: 'userRoleAssignmentListModification', - content: 'Deleted an userRoleAssignment' - }); - this.activeModal.dismiss(true); - }); - } -} - -@Component({ - selector: 'jhi-user-role-assignment-delete-popup', - template: '' -}) -export class UserRoleAssignmentDeletePopupComponent implements OnInit, OnDestroy { - protected ngbModalRef: NgbModalRef; - - constructor(protected activatedRoute: ActivatedRoute, protected router: Router, protected modalService: NgbModal) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ userRoleAssignment }) => { - setTimeout(() => { - this.ngbModalRef = this.modalService.open(UserRoleAssignmentDeleteDialogComponent as Component, { - size: 'lg', - backdrop: 'static' - }); - this.ngbModalRef.componentInstance.userRoleAssignment = userRoleAssignment; - this.ngbModalRef.result.then( - result => { - this.router.navigate(['/user-role-assignment', { outlets: { popup: null } }]); - this.ngbModalRef = null; - }, - reason => { - this.router.navigate(['/user-role-assignment', { outlets: { popup: null } }]); - this.ngbModalRef = null; - } - ); - }, 0); - }); - } - - ngOnDestroy() { - this.ngbModalRef = null; - } -} diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-detail.component.html b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-detail.component.html deleted file mode 100644 index d94896cf..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-detail.component.html +++ /dev/null @@ -1,39 +0,0 @@ -
-
-
-

User Role Assignment {{userRoleAssignment.id}}

-
- -
-
Entity Type Id
-
- {{userRoleAssignment.entityTypeId}} -
-
Entity Object Id
-
- {{userRoleAssignment.entityObjectId}} -
-
Assigned Role
-
- {{userRoleAssignment.assignedRole}} -
-
User
-
- {{userRoleAssignment.user?.login}} -
-
- - - - -
-
-
diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-detail.component.ts b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-detail.component.ts deleted file mode 100644 index 7882a634..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-detail.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { IUserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; - -@Component({ - selector: 'jhi-user-role-assignment-detail', - templateUrl: './user-role-assignment-detail.component.html' -}) -export class UserRoleAssignmentDetailComponent implements OnInit { - userRoleAssignment: IUserRoleAssignment; - - constructor(protected activatedRoute: ActivatedRoute) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({ userRoleAssignment }) => { - this.userRoleAssignment = userRoleAssignment; - }); - } - - previousState() { - window.history.back(); - } -} diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-update.component.html b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-update.component.html deleted file mode 100644 index 73dc3e8f..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-update.component.html +++ /dev/null @@ -1,77 +0,0 @@ -
-
-
-

Create or edit a User Role Assignment

-
- -
- - -
-
- - -
- - This field is required. - - - This field cannot be longer than 32 characters. - -
-
-
- - -
- - This field is required. - - - This field should be a number. - -
-
-
- - -
- - This field is required. - -
-
- -
- - -
-
-
- - -
-
-
-
diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-update.component.ts b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-update.component.ts deleted file mode 100644 index acd6dd35..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment-update.component.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { HttpResponse, HttpErrorResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { JhiAlertService } from 'ng-jhipster'; -import { IUserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; -import { UserRoleAssignmentService } from './user-role-assignment.service'; -import { IUser, UserService } from 'app/core'; - -@Component({ - selector: 'jhi-user-role-assignment-update', - templateUrl: './user-role-assignment-update.component.html' -}) -export class UserRoleAssignmentUpdateComponent implements OnInit { - userRoleAssignment: IUserRoleAssignment; - isSaving: boolean; - - users: IUser[]; - - constructor( - protected jhiAlertService: JhiAlertService, - protected userRoleAssignmentService: UserRoleAssignmentService, - protected userService: UserService, - protected activatedRoute: ActivatedRoute - ) {} - - ngOnInit() { - this.isSaving = false; - this.activatedRoute.data.subscribe(({ userRoleAssignment }) => { - this.userRoleAssignment = userRoleAssignment; - }); - this.userService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: IUser[]) => (this.users = res), (res: HttpErrorResponse) => this.onError(res.message)); - } - - previousState() { - window.history.back(); - } - - save() { - this.isSaving = true; - if (this.userRoleAssignment.id !== undefined) { - this.subscribeToSaveResponse(this.userRoleAssignmentService.update(this.userRoleAssignment)); - } else { - this.subscribeToSaveResponse(this.userRoleAssignmentService.create(this.userRoleAssignment)); - } - } - - protected subscribeToSaveResponse(result: Observable>) { - result.subscribe((res: HttpResponse) => this.onSaveSuccess(), (res: HttpErrorResponse) => this.onSaveError()); - } - - protected onSaveSuccess() { - this.isSaving = false; - this.previousState(); - } - - protected onSaveError() { - this.isSaving = false; - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } - - trackUserById(index: number, item: IUser) { - return item.id; - } -} diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.component.html b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.component.html deleted file mode 100644 index 5510b4f7..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.component.html +++ /dev/null @@ -1,90 +0,0 @@ -
-

- User Role Assignments - -

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ID Entity Type Id Entity Object Id Assigned Role User
- - - -
{{userRoleAssignment.id}}{{userRoleAssignment.entityTypeId}}{{userRoleAssignment.entityObjectId}}{{userRoleAssignment.assignedRole}} - {{userRoleAssignment.user?.login}} - -
- - - -
-
-
-
diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.component.ts b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.component.ts deleted file mode 100644 index 38fde13c..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.component.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { Subscription } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { JhiEventManager, JhiParseLinks, JhiAlertService } from 'ng-jhipster'; - -import { IUserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; -import { AccountService } from 'app/core'; - -import { ITEMS_PER_PAGE } from 'app/shared'; -import { UserRoleAssignmentService } from './user-role-assignment.service'; -import { IUser } from 'app/core/user/user.model'; -import { UserService } from 'app/core/user/user.service'; -import { TableFilter, queryEquals, queryContains } from 'app/shared/util/tablefilter'; - -@Component({ - selector: 'jhi-user-role-assignment', - templateUrl: './user-role-assignment.component.html' -}) -export class UserRoleAssignmentComponent implements OnInit, OnDestroy { - userRoleAssignments: IUserRoleAssignment[]; - currentAccount: any; - eventSubscriber: Subscription; - itemsPerPage: number; - links: any; - page: any; - predicate: any; - reverse: any; - totalItems: number; - users: IUser[]; - filter: TableFilter<{ - entityTypeId?: string; - entityObjectId?: string; - assignedRole?: string; - userId?: string; - }>; - - constructor( - protected userRoleAssignmentService: UserRoleAssignmentService, - protected userService: UserService, - protected jhiAlertService: JhiAlertService, - protected eventManager: JhiEventManager, - protected parseLinks: JhiParseLinks, - protected accountService: AccountService - ) { - this.userRoleAssignments = []; - this.itemsPerPage = ITEMS_PER_PAGE; - this.page = 0; - this.links = { - last: 0 - }; - this.predicate = 'id'; - this.reverse = true; - this.filter = new TableFilter( - { - entityTypeId: queryContains, - entityObjectId: queryEquals, - assignedRole: queryEquals, - userId: queryEquals - }, - 500, - () => { - this.reset(); - } - ); - } - - loadAll() { - this.userRoleAssignmentService - .query({ - ...this.filter.buildQueryCriteria(), - page: this.page, - size: this.itemsPerPage, - sort: this.sort() - }) - .subscribe( - (res: HttpResponse) => this.paginateUserRoleAssignments(res.body, res.headers), - (res: HttpErrorResponse) => this.onError(res.message) - ); - } - - reset() { - this.page = 0; - this.userRoleAssignments = []; - this.loadAll(); - } - - loadPage(page) { - this.page = page; - this.loadAll(); - } - - ngOnInit() { - this.loadAll(); - this.accountService.identity().then(account => { - this.currentAccount = account; - }); - this.userService - .query() - .pipe( - filter((mayBeOk: HttpResponse) => mayBeOk.ok), - map((response: HttpResponse) => response.body) - ) - .subscribe((res: IUser[]) => (this.users = res), (res: HttpErrorResponse) => this.onError(res.message)); - this.registerChangeInUserRoleAssignments(); - } - - ngOnDestroy() { - this.eventManager.destroy(this.eventSubscriber); - } - - trackId(index: number, item: { id: number }) { - return item.id; - } - - registerChangeInUserRoleAssignments() { - this.eventSubscriber = this.eventManager.subscribe('userRoleAssignmentListModification', response => this.reset()); - } - - sort() { - const result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; - if (this.predicate !== 'id') { - result.push('id'); - } - return result; - } - - protected paginateUserRoleAssignments(data: IUserRoleAssignment[], headers: HttpHeaders) { - this.links = this.parseLinks.parse(headers.get('link')); - this.totalItems = parseInt(headers.get('X-Total-Count'), 10); - for (let i = 0; i < data.length; i++) { - this.userRoleAssignments.push(data[i]); - } - } - - protected onError(errorMessage: string) { - this.jhiAlertService.error(errorMessage, null, null); - } -} diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.module.ts b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.module.ts deleted file mode 100644 index f90bd034..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { JhiLanguageService } from 'ng-jhipster'; -import { JhiLanguageHelper } from 'app/core'; - -import { HsadminNgSharedModule } from 'app/shared'; -import { - UserRoleAssignmentComponent, - UserRoleAssignmentDetailComponent, - UserRoleAssignmentUpdateComponent, - UserRoleAssignmentDeletePopupComponent, - UserRoleAssignmentDeleteDialogComponent, - userRoleAssignmentRoute, - userRoleAssignmentPopupRoute -} from './'; - -const ENTITY_STATES = [...userRoleAssignmentRoute, ...userRoleAssignmentPopupRoute]; - -@NgModule({ - imports: [HsadminNgSharedModule, RouterModule.forChild(ENTITY_STATES)], - declarations: [ - UserRoleAssignmentComponent, - UserRoleAssignmentDetailComponent, - UserRoleAssignmentUpdateComponent, - UserRoleAssignmentDeleteDialogComponent, - UserRoleAssignmentDeletePopupComponent - ], - entryComponents: [ - UserRoleAssignmentComponent, - UserRoleAssignmentUpdateComponent, - UserRoleAssignmentDeleteDialogComponent, - UserRoleAssignmentDeletePopupComponent - ], - providers: [{ provide: JhiLanguageService, useClass: JhiLanguageService }], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgUserRoleAssignmentModule { - constructor(private languageService: JhiLanguageService, private languageHelper: JhiLanguageHelper) { - this.languageHelper.language.subscribe((languageKey: string) => { - if (languageKey !== undefined) { - this.languageService.changeLanguage(languageKey); - } - }); - } -} diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.route.ts b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.route.ts deleted file mode 100644 index 7271326a..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.route.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpResponse } from '@angular/common/http'; -import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes } from '@angular/router'; -import { UserRouteAccessService } from 'app/core'; -import { Observable, of } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { UserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; -import { UserRoleAssignmentService } from './user-role-assignment.service'; -import { UserRoleAssignmentComponent } from './user-role-assignment.component'; -import { UserRoleAssignmentDetailComponent } from './user-role-assignment-detail.component'; -import { UserRoleAssignmentUpdateComponent } from './user-role-assignment-update.component'; -import { UserRoleAssignmentDeletePopupComponent } from './user-role-assignment-delete-dialog.component'; -import { IUserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; - -@Injectable({ providedIn: 'root' }) -export class UserRoleAssignmentResolve implements Resolve { - constructor(private service: UserRoleAssignmentService) {} - - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - const id = route.params['id'] ? route.params['id'] : null; - if (id) { - return this.service.find(id).pipe( - filter((response: HttpResponse) => response.ok), - map((userRoleAssignment: HttpResponse) => userRoleAssignment.body) - ); - } - return of(new UserRoleAssignment()); - } -} - -export const userRoleAssignmentRoute: Routes = [ - { - path: '', - component: UserRoleAssignmentComponent, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.userRoleAssignment.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/view', - component: UserRoleAssignmentDetailComponent, - resolve: { - userRoleAssignment: UserRoleAssignmentResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.userRoleAssignment.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: 'new', - component: UserRoleAssignmentUpdateComponent, - resolve: { - userRoleAssignment: UserRoleAssignmentResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.userRoleAssignment.home.title' - }, - canActivate: [UserRouteAccessService] - }, - { - path: ':id/edit', - component: UserRoleAssignmentUpdateComponent, - resolve: { - userRoleAssignment: UserRoleAssignmentResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.userRoleAssignment.home.title' - }, - canActivate: [UserRouteAccessService] - } -]; - -export const userRoleAssignmentPopupRoute: Routes = [ - { - path: ':id/delete', - component: UserRoleAssignmentDeletePopupComponent, - resolve: { - userRoleAssignment: UserRoleAssignmentResolve - }, - data: { - authorities: ['ROLE_USER'], - pageTitle: 'hsadminNgApp.userRoleAssignment.home.title' - }, - canActivate: [UserRouteAccessService], - outlet: 'popup' - } -]; diff --git a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.service.ts b/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.service.ts deleted file mode 100644 index ea268039..00000000 --- a/src/main/webapp/app/entities/user-role-assignment/user-role-assignment.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { createRequestOption } from 'app/shared'; -import { IUserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; - -type EntityResponseType = HttpResponse; -type EntityArrayResponseType = HttpResponse; - -@Injectable({ providedIn: 'root' }) -export class UserRoleAssignmentService { - public resourceUrl = SERVER_API_URL + 'api/user-role-assignments'; - - constructor(protected http: HttpClient) {} - - create(userRoleAssignment: IUserRoleAssignment): Observable { - return this.http.post(this.resourceUrl, userRoleAssignment, { observe: 'response' }); - } - - update(userRoleAssignment: IUserRoleAssignment): Observable { - return this.http.put(this.resourceUrl, userRoleAssignment, { observe: 'response' }); - } - - find(id: number): Observable { - return this.http.get(`${this.resourceUrl}/${id}`, { observe: 'response' }); - } - - query(req?: any): Observable { - const options = createRequestOption(req); - return this.http.get(this.resourceUrl, { params: options, observe: 'response' }); - } - - delete(id: number): Observable> { - return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); - } -} diff --git a/src/main/webapp/app/home/home.component.html b/src/main/webapp/app/home/home.component.html deleted file mode 100644 index 46417688..00000000 --- a/src/main/webapp/app/home/home.component.html +++ /dev/null @@ -1,41 +0,0 @@ -
-
- -
-
-

Welcome, Java Hipster!

-

This is your homepage

- -
-
- You are logged in as user "{{account.login}}". -
- -
- If you want to - sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and password="user").
-
-
- You don't have an account yet?  - Register a new account -
-
- -

- If you have any question on JHipster: -

- - - -

- If you like JHipster, don't forget to give us a star on GitHub! -

-
-
diff --git a/src/main/webapp/app/home/home.component.ts b/src/main/webapp/app/home/home.component.ts deleted file mode 100644 index af0e6c4e..00000000 --- a/src/main/webapp/app/home/home.component.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { JhiEventManager } from 'ng-jhipster'; - -import { LoginModalService, AccountService, Account } from 'app/core'; - -@Component({ - selector: 'jhi-home', - templateUrl: './home.component.html', - styleUrls: ['home.css'] -}) -export class HomeComponent implements OnInit { - account: Account; - modalRef: NgbModalRef; - - constructor( - private accountService: AccountService, - private loginModalService: LoginModalService, - private eventManager: JhiEventManager - ) {} - - ngOnInit() { - this.accountService.identity().then((account: Account) => { - this.account = account; - }); - this.registerAuthenticationSuccess(); - } - - registerAuthenticationSuccess() { - this.eventManager.subscribe('authenticationSuccess', message => { - this.accountService.identity().then(account => { - this.account = account; - }); - }); - } - - isAuthenticated() { - return this.accountService.isAuthenticated(); - } - - login() { - this.modalRef = this.loginModalService.open(); - } -} diff --git a/src/main/webapp/app/home/home.css b/src/main/webapp/app/home/home.css deleted file mode 100644 index 736b6999..00000000 --- a/src/main/webapp/app/home/home.css +++ /dev/null @@ -1,23 +0,0 @@ -/* ========================================================================== -Main page styles -========================================================================== */ - -.hipster { - display: inline-block; - width: 347px; - height: 497px; - background: url('../../content/images/jhipster_family_member_3.svg') no-repeat center top; - background-size: contain; -} - -/* wait autoprefixer update to allow simple generation of high pixel density media query */ -@media only screen and (-webkit-min-device-pixel-ratio: 2), - only screen and (-moz-min-device-pixel-ratio: 2), - only screen and (-o-min-device-pixel-ratio: 2/1), - only screen and (min-resolution: 192dpi), - only screen and (min-resolution: 2dppx) { - .hipster { - background: url('../../content/images/jhipster_family_member_3.svg') no-repeat center top; - background-size: contain; - } -} diff --git a/src/main/webapp/app/home/home.module.ts b/src/main/webapp/app/home/home.module.ts deleted file mode 100644 index 617df607..00000000 --- a/src/main/webapp/app/home/home.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { HsadminNgSharedModule } from 'app/shared'; -import { HOME_ROUTE, HomeComponent } from './'; - -@NgModule({ - imports: [HsadminNgSharedModule, RouterModule.forChild([HOME_ROUTE])], - declarations: [HomeComponent], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgHomeModule {} diff --git a/src/main/webapp/app/home/home.route.ts b/src/main/webapp/app/home/home.route.ts deleted file mode 100644 index cfa1a3fb..00000000 --- a/src/main/webapp/app/home/home.route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Route } from '@angular/router'; - -import { HomeComponent } from './'; - -export const HOME_ROUTE: Route = { - path: '', - component: HomeComponent, - data: { - authorities: [], - pageTitle: 'home.title' - } -}; diff --git a/src/main/webapp/app/home/index.ts b/src/main/webapp/app/home/index.ts deleted file mode 100644 index d76285b2..00000000 --- a/src/main/webapp/app/home/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './home.component'; -export * from './home.route'; -export * from './home.module'; diff --git a/src/main/webapp/app/layouts/error/error.component.html b/src/main/webapp/app/layouts/error/error.component.html deleted file mode 100644 index 64a40392..00000000 --- a/src/main/webapp/app/layouts/error/error.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
-
-
- -
-
-

Error Page!

- -
-
{{errorMessage}} -
-
-
You are not authorized to access this page. -
-
The page asked was not found. -
-
-
-
diff --git a/src/main/webapp/app/layouts/error/error.component.ts b/src/main/webapp/app/layouts/error/error.component.ts deleted file mode 100644 index faa96581..00000000 --- a/src/main/webapp/app/layouts/error/error.component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -@Component({ - selector: 'jhi-error', - templateUrl: './error.component.html' -}) -export class ErrorComponent implements OnInit { - errorMessage: string; - error403: boolean; - error404: boolean; - - constructor(private route: ActivatedRoute) {} - - ngOnInit() { - this.route.data.subscribe(routeData => { - if (routeData.error403) { - this.error403 = routeData.error403; - } - if (routeData.error404) { - this.error404 = routeData.error404; - } - if (routeData.errorMessage) { - this.errorMessage = routeData.errorMessage; - } - }); - } -} diff --git a/src/main/webapp/app/layouts/error/error.route.ts b/src/main/webapp/app/layouts/error/error.route.ts deleted file mode 100644 index f40b4c7c..00000000 --- a/src/main/webapp/app/layouts/error/error.route.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Routes } from '@angular/router'; - -import { ErrorComponent } from './error.component'; - -export const errorRoute: Routes = [ - { - path: 'error', - component: ErrorComponent, - data: { - authorities: [], - pageTitle: 'error.title' - } - }, - { - path: 'accessdenied', - component: ErrorComponent, - data: { - authorities: [], - pageTitle: 'error.title', - error403: true - } - }, - { - path: '404', - component: ErrorComponent, - data: { - authorities: [], - pageTitle: 'error.title', - error404: true - } - }, - { - path: '**', - redirectTo: '/404' - } -]; diff --git a/src/main/webapp/app/layouts/footer/footer.component.html b/src/main/webapp/app/layouts/footer/footer.component.html deleted file mode 100644 index 9312ad06..00000000 --- a/src/main/webapp/app/layouts/footer/footer.component.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/src/main/webapp/app/layouts/footer/footer.component.ts b/src/main/webapp/app/layouts/footer/footer.component.ts deleted file mode 100644 index 37da8bca..00000000 --- a/src/main/webapp/app/layouts/footer/footer.component.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'jhi-footer', - templateUrl: './footer.component.html' -}) -export class FooterComponent {} diff --git a/src/main/webapp/app/layouts/index.ts b/src/main/webapp/app/layouts/index.ts deleted file mode 100644 index d7432602..00000000 --- a/src/main/webapp/app/layouts/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from './error/error.component'; -export * from './error/error.route'; -export * from './main/main.component'; -export * from './footer/footer.component'; -export * from './navbar/navbar.component'; -export * from './navbar/navbar.route'; -export * from './navbar/active-menu.directive'; -export * from './profiles/page-ribbon.component'; -export * from './profiles/profile.service'; -export * from './profiles/profile-info.model'; diff --git a/src/main/webapp/app/layouts/main/main.component.html b/src/main/webapp/app/layouts/main/main.component.html deleted file mode 100644 index 5bcd12ab..00000000 --- a/src/main/webapp/app/layouts/main/main.component.html +++ /dev/null @@ -1,11 +0,0 @@ - -
- -
-
-
- - -
- -
diff --git a/src/main/webapp/app/layouts/main/main.component.ts b/src/main/webapp/app/layouts/main/main.component.ts deleted file mode 100644 index 4aa553bf..00000000 --- a/src/main/webapp/app/layouts/main/main.component.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Router, ActivatedRouteSnapshot, NavigationEnd, NavigationError } from '@angular/router'; - -import { JhiLanguageHelper } from 'app/core'; - -@Component({ - selector: 'jhi-main', - templateUrl: './main.component.html' -}) -export class JhiMainComponent implements OnInit { - constructor(private jhiLanguageHelper: JhiLanguageHelper, private router: Router) {} - - private getPageTitle(routeSnapshot: ActivatedRouteSnapshot) { - let title: string = routeSnapshot.data && routeSnapshot.data['pageTitle'] ? routeSnapshot.data['pageTitle'] : 'hsadminNgApp'; - if (routeSnapshot.firstChild) { - title = this.getPageTitle(routeSnapshot.firstChild) || title; - } - return title; - } - - ngOnInit() { - this.router.events.subscribe(event => { - if (event instanceof NavigationEnd) { - this.jhiLanguageHelper.updateTitle(this.getPageTitle(this.router.routerState.snapshot.root)); - } - if (event instanceof NavigationError && event.error.status === 404) { - this.router.navigate(['/404']); - } - }); - } -} diff --git a/src/main/webapp/app/layouts/navbar/active-menu.directive.ts b/src/main/webapp/app/layouts/navbar/active-menu.directive.ts deleted file mode 100644 index edbdeb94..00000000 --- a/src/main/webapp/app/layouts/navbar/active-menu.directive.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Directive, OnInit, ElementRef, Renderer, Input } from '@angular/core'; -import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; - -@Directive({ - selector: '[jhiActiveMenu]' -}) -export class ActiveMenuDirective implements OnInit { - @Input() jhiActiveMenu: string; - - constructor(private el: ElementRef, private renderer: Renderer, private translateService: TranslateService) {} - - ngOnInit() { - this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { - this.updateActiveFlag(event.lang); - }); - this.updateActiveFlag(this.translateService.currentLang); - } - - updateActiveFlag(selectedLanguage) { - if (this.jhiActiveMenu === selectedLanguage) { - this.renderer.setElementClass(this.el.nativeElement, 'active', true); - } else { - this.renderer.setElementClass(this.el.nativeElement, 'active', false); - } - } -} diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.html b/src/main/webapp/app/layouts/navbar/navbar.component.html deleted file mode 100644 index cf34be25..00000000 --- a/src/main/webapp/app/layouts/navbar/navbar.component.html +++ /dev/null @@ -1,188 +0,0 @@ - diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.ts b/src/main/webapp/app/layouts/navbar/navbar.component.ts deleted file mode 100644 index 52873680..00000000 --- a/src/main/webapp/app/layouts/navbar/navbar.component.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { JhiLanguageService } from 'ng-jhipster'; -import { SessionStorageService } from 'ngx-webstorage'; - -import { VERSION } from 'app/app.constants'; -import { JhiLanguageHelper, AccountService, LoginModalService, LoginService } from 'app/core'; -import { ProfileService } from 'app/layouts/profiles/profile.service'; - -@Component({ - selector: 'jhi-navbar', - templateUrl: './navbar.component.html', - styleUrls: ['navbar.css'] -}) -export class NavbarComponent implements OnInit { - inProduction: boolean; - isNavbarCollapsed: boolean; - languages: any[]; - swaggerEnabled: boolean; - modalRef: NgbModalRef; - version: string; - - constructor( - private loginService: LoginService, - private languageService: JhiLanguageService, - private languageHelper: JhiLanguageHelper, - private sessionStorage: SessionStorageService, - private accountService: AccountService, - private loginModalService: LoginModalService, - private profileService: ProfileService, - private router: Router - ) { - this.version = VERSION ? 'v' + VERSION : ''; - this.isNavbarCollapsed = true; - } - - ngOnInit() { - this.languageHelper.getAll().then(languages => { - this.languages = languages; - }); - - this.profileService.getProfileInfo().then(profileInfo => { - this.inProduction = profileInfo.inProduction; - this.swaggerEnabled = profileInfo.swaggerEnabled; - }); - } - - changeLanguage(languageKey: string) { - this.sessionStorage.store('locale', languageKey); - this.languageService.changeLanguage(languageKey); - } - - collapseNavbar() { - this.isNavbarCollapsed = true; - } - - isAuthenticated() { - return this.accountService.isAuthenticated(); - } - - login() { - this.modalRef = this.loginModalService.open(); - } - - logout() { - this.collapseNavbar(); - this.loginService.logout(); - this.router.navigate(['']); - } - - toggleNavbar() { - this.isNavbarCollapsed = !this.isNavbarCollapsed; - } - - getImageUrl() { - return this.isAuthenticated() ? this.accountService.getImageUrl() : null; - } -} diff --git a/src/main/webapp/app/layouts/navbar/navbar.css b/src/main/webapp/app/layouts/navbar/navbar.css deleted file mode 100644 index dccbee08..00000000 --- a/src/main/webapp/app/layouts/navbar/navbar.css +++ /dev/null @@ -1,89 +0,0 @@ -/* ========================================================================== -Navbar -========================================================================== */ -.navbar-version { - font-size: 10px; - color: #ccc; -} - -.jh-navbar { - background-color: #353d47; - padding: 0.2em 1em; -} - -.jh-navbar .profile-image { - margin: -10px 0px; - height: 40px; - width: 40px; - border-radius: 50%; -} - -.jh-navbar .dropdown-item.active, -.jh-navbar .dropdown-item.active:focus, -.jh-navbar .dropdown-item.active:hover { - background-color: #353d47; -} - -.jh-navbar .dropdown-toggle::after { - margin-left: 0.15em; -} - -.jh-navbar ul.navbar-nav { - padding: 0.5em; -} - -.jh-navbar .navbar-nav .nav-item { - margin-left: 1.5rem; -} - -.jh-navbar a.nav-link { - font-weight: 400; -} - -.jh-navbar .jh-navbar-toggler { - color: #ccc; - font-size: 1.5em; - padding: 10px; -} - -.jh-navbar .jh-navbar-toggler:hover { - color: #fff; -} - -@media screen and (min-width: 768px) { - .jh-navbar-toggler { - display: none; - } -} - -@media screen and (max-width: 992px) { - .jh-logo-container { - width: 100%; - } -} - -.navbar-title { - display: inline-block; - vertical-align: middle; -} - -/* ========================================================================== -Logo styles -========================================================================== */ -.navbar-brand.logo { - padding: 5px 15px; -} - -.logo-img { - height: 100%; - background: url('../../../content/images/logo-jhipster.png') no-repeat center center; - background-size: contain; - width: 100%; -} - -.logo .logo-img { - height: 45px; - display: inline-block; - vertical-align: middle; - width: 70px; -} diff --git a/src/main/webapp/app/layouts/navbar/navbar.route.ts b/src/main/webapp/app/layouts/navbar/navbar.route.ts deleted file mode 100644 index 317d9960..00000000 --- a/src/main/webapp/app/layouts/navbar/navbar.route.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Route } from '@angular/router'; - -import { NavbarComponent } from './navbar.component'; - -export const navbarRoute: Route = { - path: '', - component: NavbarComponent, - outlet: 'navbar' -}; diff --git a/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts b/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts deleted file mode 100644 index bcdb5069..00000000 --- a/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ProfileService } from './profile.service'; -import { ProfileInfo } from './profile-info.model'; - -@Component({ - selector: 'jhi-page-ribbon', - template: ` - - `, - styleUrls: ['page-ribbon.css'] -}) -export class PageRibbonComponent implements OnInit { - profileInfo: ProfileInfo; - ribbonEnv: string; - - constructor(private profileService: ProfileService) {} - - ngOnInit() { - this.profileService.getProfileInfo().then(profileInfo => { - this.profileInfo = profileInfo; - this.ribbonEnv = profileInfo.ribbonEnv; - }); - } -} diff --git a/src/main/webapp/app/layouts/profiles/page-ribbon.css b/src/main/webapp/app/layouts/profiles/page-ribbon.css deleted file mode 100644 index b07c9efe..00000000 --- a/src/main/webapp/app/layouts/profiles/page-ribbon.css +++ /dev/null @@ -1,32 +0,0 @@ -/* ========================================================================== -Developement Ribbon -========================================================================== */ -.ribbon { - background-color: rgba(170, 0, 0, 0.5); - left: -3.5em; - -moz-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - -webkit-transform: rotate(-45deg); - transform: rotate(-45deg); - overflow: hidden; - position: absolute; - top: 40px; - white-space: nowrap; - width: 15em; - z-index: 9999; - pointer-events: none; - opacity: 0.75; -} - -.ribbon a { - color: #fff; - display: block; - font-weight: 400; - margin: 1px 0; - padding: 10px 50px; - text-align: center; - text-decoration: none; - text-shadow: 0 0 5px #444; - pointer-events: none; -} diff --git a/src/main/webapp/app/layouts/profiles/profile-info.model.ts b/src/main/webapp/app/layouts/profiles/profile-info.model.ts deleted file mode 100644 index f1adc52c..00000000 --- a/src/main/webapp/app/layouts/profiles/profile-info.model.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class ProfileInfo { - activeProfiles: string[]; - ribbonEnv: string; - inProduction: boolean; - swaggerEnabled: boolean; -} diff --git a/src/main/webapp/app/layouts/profiles/profile.service.ts b/src/main/webapp/app/layouts/profiles/profile.service.ts deleted file mode 100644 index d07fad7e..00000000 --- a/src/main/webapp/app/layouts/profiles/profile.service.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; - -import { SERVER_API_URL } from 'app/app.constants'; -import { ProfileInfo } from './profile-info.model'; -import { map } from 'rxjs/operators'; - -@Injectable({ providedIn: 'root' }) -export class ProfileService { - private infoUrl = SERVER_API_URL + 'management/info'; - private profileInfo: Promise; - - constructor(private http: HttpClient) {} - - getProfileInfo(): Promise { - if (!this.profileInfo) { - this.profileInfo = this.http - .get(this.infoUrl, { observe: 'response' }) - .pipe( - map((res: HttpResponse) => { - const data = res.body; - const pi = new ProfileInfo(); - pi.activeProfiles = data['activeProfiles']; - const displayRibbonOnProfiles = data['display-ribbon-on-profiles'].split(','); - if (pi.activeProfiles) { - const ribbonProfiles = displayRibbonOnProfiles.filter(profile => pi.activeProfiles.includes(profile)); - if (ribbonProfiles.length !== 0) { - pi.ribbonEnv = ribbonProfiles[0]; - } - pi.inProduction = pi.activeProfiles.includes('prod'); - pi.swaggerEnabled = pi.activeProfiles.includes('swagger'); - } - return pi; - }) - ) - .toPromise(); - } - return this.profileInfo; - } -} diff --git a/src/main/webapp/app/polyfills.ts b/src/main/webapp/app/polyfills.ts deleted file mode 100644 index cf38f322..00000000 --- a/src/main/webapp/app/polyfills.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), - * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. - * - * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** IE9, IE10 and IE11 requires all of the following polyfills. **/ -import 'core-js/es6/symbol'; -import 'core-js/es6/object'; -import 'core-js/es6/function'; -import 'core-js/es6/parse-int'; -import 'core-js/es6/parse-float'; -import 'core-js/es6/number'; -import 'core-js/es6/math'; -import 'core-js/es6/string'; -import 'core-js/es6/date'; -import 'core-js/es6/array'; -import 'core-js/es7/array'; -import 'core-js/es6/regexp'; -import 'core-js/es6/map'; -import 'core-js/es6/weak-map'; -import 'core-js/es6/set'; - -/** IE10 and IE11 requires the following for NgClass support on SVG elements */ -// import 'classlist.js'; // Run `npm install --save classlist.js`. - -/** Evergreen browsers require these. **/ -import 'core-js/es6/reflect'; -import 'core-js/es7/reflect'; - -/** - * Required to support Web Animations `@angular/animation`. - * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation - **/ -// import 'web-animations-js'; // Run `npm install --save web-animations-js`. - -/*************************************************************************************************** - * Zone JS is required by Angular itself. - */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ - -/** - * Date, currency, decimal and percent pipes. - * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 - */ -// import 'intl'; // Run `npm install --save intl`. -/** - * Need to import at least one locale-data with intl. - */ -// import 'intl/locale-data/jsonp/en'; - -require('../manifest.webapp'); diff --git a/src/main/webapp/app/shared/alert/alert-error.component.ts b/src/main/webapp/app/shared/alert/alert-error.component.ts deleted file mode 100644 index 248bc75d..00000000 --- a/src/main/webapp/app/shared/alert/alert-error.component.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Component, OnDestroy } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { JhiEventManager, JhiAlert, JhiAlertService } from 'ng-jhipster'; -import { Subscription } from 'rxjs'; - -@Component({ - selector: 'jhi-alert-error', - template: ` - - ` -}) -export class JhiAlertErrorComponent implements OnDestroy { - alerts: any[]; - cleanHttpErrorListener: Subscription; - /* tslint:disable */ - constructor(private alertService: JhiAlertService, private eventManager: JhiEventManager, private translateService: TranslateService) { - /* tslint:enable */ - this.alerts = []; - - this.cleanHttpErrorListener = eventManager.subscribe('hsadminNgApp.httpError', response => { - let i; - const httpErrorResponse = response.content; - switch (httpErrorResponse.status) { - // connection refused, server not reachable - case 0: - this.addErrorAlert('Server not reachable', 'error.server.not.reachable'); - break; - - case 400: - const arr = httpErrorResponse.headers.keys(); - let errorHeader = null; - let entityKey = null; - arr.forEach(entry => { - if (entry.toLowerCase().endsWith('app-error')) { - errorHeader = httpErrorResponse.headers.get(entry); - } else if (entry.toLowerCase().endsWith('app-params')) { - entityKey = httpErrorResponse.headers.get(entry); - } - }); - if (errorHeader) { - const entityName = translateService.instant('global.menu.entities.' + entityKey); - this.addErrorAlert(errorHeader, errorHeader, { entityName }); - } else if (httpErrorResponse.error !== '' && httpErrorResponse.error.fieldErrors) { - const fieldErrors = httpErrorResponse.error.fieldErrors; - for (i = 0; i < fieldErrors.length; i++) { - const fieldError = fieldErrors[i]; - if (['Min', 'Max', 'DecimalMin', 'DecimalMax'].includes(fieldError.message)) { - fieldError.message = 'Size'; - } - // convert 'something[14].other[4].id' to 'something[].other[].id' so translations can be written to it - const convertedField = fieldError.field.replace(/\[\d*\]/g, '[]'); - const fieldName = translateService.instant('hsadminNgApp.' + fieldError.objectName + '.' + convertedField); - this.addErrorAlert('Error on field "' + fieldName + '"', 'error.' + fieldError.message, { fieldName }); - } - } else if (httpErrorResponse.error !== '' && httpErrorResponse.error.message) { - this.addErrorAlert( - httpErrorResponse.error.message, - httpErrorResponse.error.message, - httpErrorResponse.error.params - ); - } else { - this.addErrorAlert(httpErrorResponse.error); - } - break; - - case 404: - this.addErrorAlert('Not found', 'error.url.not.found'); - break; - - default: - if (httpErrorResponse.error !== '' && httpErrorResponse.error.message) { - this.addErrorAlert(httpErrorResponse.error.message); - } else { - this.addErrorAlert(httpErrorResponse.error); - } - } - }); - } - - setClasses(alert) { - return { - toast: !!alert.toast, - [alert.position]: true - }; - } - - ngOnDestroy() { - if (this.cleanHttpErrorListener !== undefined && this.cleanHttpErrorListener !== null) { - this.eventManager.destroy(this.cleanHttpErrorListener); - this.alerts = []; - } - } - - addErrorAlert(message, key?, data?) { - message = key && key !== null ? key : message; - - const newAlert: JhiAlert = { - type: 'danger', - msg: message, - params: data, - timeout: 5000, - toast: this.alertService.isToast(), - scoped: true - }; - - this.alerts.push(this.alertService.addAlert(newAlert, this.alerts)); - } -} diff --git a/src/main/webapp/app/shared/alert/alert.component.ts b/src/main/webapp/app/shared/alert/alert.component.ts deleted file mode 100644 index a77c3e72..00000000 --- a/src/main/webapp/app/shared/alert/alert.component.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { JhiAlertService } from 'ng-jhipster'; - -@Component({ - selector: 'jhi-alert', - template: ` - - ` -}) -export class JhiAlertComponent implements OnInit, OnDestroy { - alerts: any[]; - - constructor(private alertService: JhiAlertService) {} - - ngOnInit() { - this.alerts = this.alertService.get(); - } - - setClasses(alert) { - return { - toast: !!alert.toast, - [alert.position]: true - }; - } - - ngOnDestroy() { - this.alerts = []; - } -} diff --git a/src/main/webapp/app/shared/auth/has-any-authority.directive.ts b/src/main/webapp/app/shared/auth/has-any-authority.directive.ts deleted file mode 100644 index 0f8cefb2..00000000 --- a/src/main/webapp/app/shared/auth/has-any-authority.directive.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; -import { AccountService } from 'app/core/auth/account.service'; - -/** - * @whatItDoes Conditionally includes an HTML element if current user has any - * of the authorities passed as the `expression`. - * - * @howToUse - * ``` - * ... - * - * ... - * ``` - */ -@Directive({ - selector: '[jhiHasAnyAuthority]' -}) -export class HasAnyAuthorityDirective { - private authorities: string[]; - - constructor( - private accountService: AccountService, - private templateRef: TemplateRef, - private viewContainerRef: ViewContainerRef - ) {} - - @Input() - set jhiHasAnyAuthority(value: string | string[]) { - this.authorities = typeof value === 'string' ? [value] : value; - this.updateView(); - // Get notified each time authentication state changes. - this.accountService.getAuthenticationState().subscribe(identity => this.updateView()); - } - - private updateView(): void { - const hasAnyAuthority = this.accountService.hasAnyAuthority(this.authorities); - this.viewContainerRef.clear(); - if (hasAnyAuthority) { - this.viewContainerRef.createEmbeddedView(this.templateRef); - } - } -} diff --git a/src/main/webapp/app/shared/constants/error.constants.ts b/src/main/webapp/app/shared/constants/error.constants.ts deleted file mode 100644 index 2ebea942..00000000 --- a/src/main/webapp/app/shared/constants/error.constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const PROBLEM_BASE_URL = 'https://www.jhipster.tech/problem'; -export const EMAIL_ALREADY_USED_TYPE = PROBLEM_BASE_URL + '/email-already-used'; -export const LOGIN_ALREADY_USED_TYPE = PROBLEM_BASE_URL + '/login-already-used'; -export const EMAIL_NOT_FOUND_TYPE = PROBLEM_BASE_URL + '/email-not-found'; diff --git a/src/main/webapp/app/shared/constants/input.constants.ts b/src/main/webapp/app/shared/constants/input.constants.ts deleted file mode 100644 index 1e3978a9..00000000 --- a/src/main/webapp/app/shared/constants/input.constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const DATE_FORMAT = 'YYYY-MM-DD'; -export const DATE_TIME_FORMAT = 'YYYY-MM-DDTHH:mm'; diff --git a/src/main/webapp/app/shared/constants/pagination.constants.ts b/src/main/webapp/app/shared/constants/pagination.constants.ts deleted file mode 100644 index 1d1b27bb..00000000 --- a/src/main/webapp/app/shared/constants/pagination.constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -// For infinite scroll, it should be more than fits into a window height. -// Otherwise, the scrollbar might not be there at all, and further pages can't be reached. -export const ITEMS_PER_PAGE = 100; diff --git a/src/main/webapp/app/shared/index.ts b/src/main/webapp/app/shared/index.ts deleted file mode 100644 index 27d7cc07..00000000 --- a/src/main/webapp/app/shared/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from './constants/error.constants'; -export * from './constants/pagination.constants'; -export * from './constants/input.constants'; -export * from './alert/alert.component'; -export * from './alert/alert-error.component'; -export * from './auth/has-any-authority.directive'; -export * from './language/find-language-from-key.pipe'; -export * from './login/login.component'; -export * from './util/request-util'; -export * from './shared-libs.module'; -export * from './shared-common.module'; -export * from './shared.module'; -export * from './util/datepicker-adapter'; diff --git a/src/main/webapp/app/shared/language/find-language-from-key.pipe.ts b/src/main/webapp/app/shared/language/find-language-from-key.pipe.ts deleted file mode 100644 index 2cce488e..00000000 --- a/src/main/webapp/app/shared/language/find-language-from-key.pipe.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; - -@Pipe({ name: 'findLanguageFromKey' }) -export class FindLanguageFromKeyPipe implements PipeTransform { - private languages: any = { - en: { name: 'English' }, - de: { name: 'Deutsch' } - // jhipster-needle-i18n-language-key-pipe - JHipster will add/remove languages in this object - }; - transform(lang: string): string { - return this.languages[lang].name; - } -} diff --git a/src/main/webapp/app/shared/login/login.component.html b/src/main/webapp/app/shared/login/login.component.html deleted file mode 100644 index 6112e0a6..00000000 --- a/src/main/webapp/app/shared/login/login.component.html +++ /dev/null @@ -1,43 +0,0 @@ - - diff --git a/src/main/webapp/app/shared/login/login.component.ts b/src/main/webapp/app/shared/login/login.component.ts deleted file mode 100644 index 46711a06..00000000 --- a/src/main/webapp/app/shared/login/login.component.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Component, AfterViewInit, Renderer, ElementRef } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Router } from '@angular/router'; -import { JhiEventManager } from 'ng-jhipster'; - -import { LoginService } from 'app/core/login/login.service'; -import { StateStorageService } from 'app/core/auth/state-storage.service'; - -@Component({ - selector: 'jhi-login-modal', - templateUrl: './login.component.html' -}) -export class JhiLoginModalComponent implements AfterViewInit { - authenticationError: boolean; - password: string; - rememberMe: boolean; - username: string; - credentials: any; - - constructor( - private eventManager: JhiEventManager, - private loginService: LoginService, - private stateStorageService: StateStorageService, - private elementRef: ElementRef, - private renderer: Renderer, - private router: Router, - public activeModal: NgbActiveModal - ) { - this.credentials = {}; - } - - ngAfterViewInit() { - setTimeout(() => this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#username'), 'focus', []), 0); - } - - cancel() { - this.credentials = { - username: null, - password: null, - rememberMe: true - }; - this.authenticationError = false; - this.activeModal.dismiss('cancel'); - } - - login() { - this.loginService - .login({ - username: this.username, - password: this.password, - rememberMe: this.rememberMe - }) - .then(() => { - this.authenticationError = false; - this.activeModal.dismiss('login success'); - if (this.router.url === '/register' || /^\/activate\//.test(this.router.url) || /^\/reset\//.test(this.router.url)) { - this.router.navigate(['']); - } - - this.eventManager.broadcast({ - name: 'authenticationSuccess', - content: 'Sending Authentication Success' - }); - - // previousState was set in the authExpiredInterceptor before being redirected to login modal. - // since login is successful, go to stored previousState and clear previousState - const redirect = this.stateStorageService.getUrl(); - if (redirect) { - this.stateStorageService.storeUrl(null); - this.router.navigate([redirect]); - } - }) - .catch(() => { - this.authenticationError = true; - }); - } - - register() { - this.activeModal.dismiss('to state register'); - this.router.navigate(['/register']); - } - - requestResetPassword() { - this.activeModal.dismiss('to state requestReset'); - this.router.navigate(['/reset', 'request']); - } -} diff --git a/src/main/webapp/app/shared/model/asset.model.ts b/src/main/webapp/app/shared/model/asset.model.ts deleted file mode 100644 index f3beb759..00000000 --- a/src/main/webapp/app/shared/model/asset.model.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Moment } from 'moment'; - -export const enum AssetAction { - PAYMENT = 'PAYMENT', - HANDOVER = 'HANDOVER', - ADOPTION = 'ADOPTION', - LOSS = 'LOSS', - CLEARING = 'CLEARING', - PAYBACK = 'PAYBACK' -} - -export interface IAsset { - id?: number; - documentDate?: Moment; - valueDate?: Moment; - action?: AssetAction; - amount?: number; - remark?: string; - membershipId?: number; - membershipDisplayLabel?: string; -} - -export class Asset implements IAsset { - constructor( - public id?: number, - public documentDate?: Moment, - public valueDate?: Moment, - public action?: AssetAction, - public amount?: number, - public remark?: string, - public membershipId?: number, - public membershipDisplayLabel?: string - ) {} -} diff --git a/src/main/webapp/app/shared/model/customer.model.ts b/src/main/webapp/app/shared/model/customer.model.ts deleted file mode 100644 index 95a2e895..00000000 --- a/src/main/webapp/app/shared/model/customer.model.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Moment } from 'moment'; -import { IMembership } from 'app/shared/model/membership.model'; -import { ISepaMandate } from 'app/shared/model/sepa-mandate.model'; - -export const enum CustomerKind { - NATURAL = 'NATURAL', - LEGAL = 'LEGAL' -} - -export const enum VatRegion { - DOMESTIC = 'DOMESTIC', - EU = 'EU', - OTHER = 'OTHER' -} - -export interface ICustomer { - id?: number; - reference?: number; - prefix?: string; - name?: string; - kind?: CustomerKind; - birthDate?: Moment; - birthPlace?: string; - registrationCourt?: string; - registrationNumber?: string; - vatRegion?: VatRegion; - vatNumber?: string; - contractualSalutation?: string; - contractualAddress?: string; - billingSalutation?: string; - billingAddress?: string; - remark?: string; - memberships?: IMembership[]; - sepamandates?: ISepaMandate[]; - displayLabel?: string; -} - -export class Customer implements ICustomer { - constructor( - public id?: number, - public reference?: number, - public prefix?: string, - public name?: string, - public kind?: CustomerKind, - public birthDate?: Moment, - public birthPlace?: string, - public registrationCourt?: string, - public registrationNumber?: string, - public vatRegion?: VatRegion, - public vatNumber?: string, - public contractualSalutation?: string, - public contractualAddress?: string, - public billingSalutation?: string, - public billingAddress?: string, - public remark?: string, - public memberships?: IMembership[], - public sepamandates?: ISepaMandate[], - public displayLabel?: string - ) {} -} diff --git a/src/main/webapp/app/shared/model/membership.model.ts b/src/main/webapp/app/shared/model/membership.model.ts deleted file mode 100644 index 0e9c9680..00000000 --- a/src/main/webapp/app/shared/model/membership.model.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Moment } from 'moment'; -import { IShare } from 'app/shared/model/share.model'; -import { IAsset } from 'app/shared/model/asset.model'; - -export interface IMembership { - id?: number; - admissionDocumentDate?: Moment; - cancellationDocumentDate?: Moment; - memberFromDate?: Moment; - memberUntilDate?: Moment; - remark?: string; - shares?: IShare[]; - assets?: IAsset[]; - customerPrefix?: string; - customerId?: number; - customerDisplayLabel?: string; - displayLabel?: string; -} - -export class Membership implements IMembership { - constructor( - public id?: number, - public admissionDocumentDate?: Moment, - public cancellationDocumentDate?: Moment, - public memberFromDate?: Moment, - public memberUntilDate?: Moment, - public remark?: string, - public shares?: IShare[], - public assets?: IAsset[], - public customerPrefix?: string, - public customerId?: number, - public customerDisplayLabel?: string, - public displayLabel?: string - ) {} -} diff --git a/src/main/webapp/app/shared/model/sepa-mandate.model.ts b/src/main/webapp/app/shared/model/sepa-mandate.model.ts deleted file mode 100644 index 57aa32b4..00000000 --- a/src/main/webapp/app/shared/model/sepa-mandate.model.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Moment } from 'moment'; - -export interface ISepaMandate { - id?: number; - reference?: string; - iban?: string; - bic?: string; - grantingDocumentDate?: Moment; - revokationDocumentDate?: Moment; - validFromDate?: Moment; - validUntilDate?: Moment; - lastUsedDate?: Moment; - remark?: string; - customerDisplayLabel?: string; - customerId?: number; -} - -export class SepaMandate implements ISepaMandate { - constructor( - public id?: number, - public reference?: string, - public iban?: string, - public bic?: string, - public grantingDocumentDate?: Moment, - public revokationDocumentDate?: Moment, - public validFromDate?: Moment, - public validUntilDate?: Moment, - public lastUsedDate?: Moment, - public remark?: string, - public customerDisplayLabel?: string, - public customerId?: number - ) {} -} diff --git a/src/main/webapp/app/shared/model/share.model.ts b/src/main/webapp/app/shared/model/share.model.ts deleted file mode 100644 index af0a23b7..00000000 --- a/src/main/webapp/app/shared/model/share.model.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Moment } from 'moment'; - -export const enum ShareAction { - SUBSCRIPTION = 'SUBSCRIPTION', - CANCELLATION = 'CANCELLATION' -} - -export interface IShare { - id?: number; - documentDate?: Moment; - valueDate?: Moment; - action?: ShareAction; - quantity?: number; - remark?: string; - membershipDisplayLabel?: string; - membershipId?: number; -} - -export class Share implements IShare { - constructor( - public id?: number, - public documentDate?: Moment, - public valueDate?: Moment, - public action?: ShareAction, - public quantity?: number, - public remark?: string, - public membershipDisplayLabel?: string, - public membershipId?: number - ) {} -} diff --git a/src/main/webapp/app/shared/model/user-role-assignment.model.ts b/src/main/webapp/app/shared/model/user-role-assignment.model.ts deleted file mode 100644 index 27a4b757..00000000 --- a/src/main/webapp/app/shared/model/user-role-assignment.model.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { IUser } from 'app/core/user/user.model'; - -export const enum UserRole { - HOSTMASTER = 'HOSTMASTER', - ADMIN = 'ADMIN', - SUPPORTER = 'SUPPORTER', - CONTRACTUAL_CONTACT = 'CONTRACTUAL_CONTACT', - FINANCIAL_CONTACT = 'FINANCIAL_CONTACT', - TECHNICAL_CONTACT = 'TECHNICAL_CONTACT', - CUSTOMER_USER = 'CUSTOMER_USER' -} - -export interface IUserRoleAssignment { - id?: number; - entityTypeId?: string; - entityObjectId?: number; - assignedRole?: UserRole; - user?: IUser; -} - -export class UserRoleAssignment implements IUserRoleAssignment { - constructor( - public id?: number, - public entityTypeId?: string, - public entityObjectId?: number, - public assignedRole?: UserRole, - public user?: IUser - ) {} -} diff --git a/src/main/webapp/app/shared/shared-common.module.ts b/src/main/webapp/app/shared/shared-common.module.ts deleted file mode 100644 index 9741c2d3..00000000 --- a/src/main/webapp/app/shared/shared-common.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { FindLanguageFromKeyPipe, HsadminNgSharedLibsModule, JhiAlertComponent, JhiAlertErrorComponent } from './'; -import { LinebreaksPipe } from 'app/shared/util/linebreaks-pipe'; - -@NgModule({ - imports: [HsadminNgSharedLibsModule], - declarations: [FindLanguageFromKeyPipe, LinebreaksPipe, JhiAlertComponent, JhiAlertErrorComponent], - exports: [HsadminNgSharedLibsModule, FindLanguageFromKeyPipe, LinebreaksPipe, JhiAlertComponent, JhiAlertErrorComponent] -}) -export class HsadminNgSharedCommonModule {} diff --git a/src/main/webapp/app/shared/shared-libs.module.ts b/src/main/webapp/app/shared/shared-libs.module.ts deleted file mode 100644 index 8d55a5da..00000000 --- a/src/main/webapp/app/shared/shared-libs.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { CommonModule } from '@angular/common'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { NgJhipsterModule } from 'ng-jhipster'; -import { InfiniteScrollModule } from 'ngx-infinite-scroll'; -import { CookieModule } from 'ngx-cookie'; -import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; - -@NgModule({ - imports: [NgbModule.forRoot(), InfiniteScrollModule, CookieModule.forRoot(), FontAwesomeModule], - exports: [FormsModule, CommonModule, NgbModule, NgJhipsterModule, InfiniteScrollModule, FontAwesomeModule] -}) -export class HsadminNgSharedLibsModule { - static forRoot() { - return { - ngModule: HsadminNgSharedLibsModule - }; - } -} diff --git a/src/main/webapp/app/shared/shared.module.ts b/src/main/webapp/app/shared/shared.module.ts deleted file mode 100644 index 072b67d3..00000000 --- a/src/main/webapp/app/shared/shared.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; - -import { NgbDateMomentAdapter } from './util/datepicker-adapter'; -import { HsadminNgSharedLibsModule, HsadminNgSharedCommonModule, JhiLoginModalComponent, HasAnyAuthorityDirective } from './'; - -@NgModule({ - imports: [HsadminNgSharedLibsModule, HsadminNgSharedCommonModule], - declarations: [JhiLoginModalComponent, HasAnyAuthorityDirective], - providers: [{ provide: NgbDateAdapter, useClass: NgbDateMomentAdapter }], - entryComponents: [JhiLoginModalComponent], - exports: [HsadminNgSharedCommonModule, JhiLoginModalComponent, HasAnyAuthorityDirective], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) -export class HsadminNgSharedModule { - static forRoot() { - return { - ngModule: HsadminNgSharedModule - }; - } -} diff --git a/src/main/webapp/app/shared/util/datepicker-adapter.ts b/src/main/webapp/app/shared/util/datepicker-adapter.ts deleted file mode 100644 index 524a38c8..00000000 --- a/src/main/webapp/app/shared/util/datepicker-adapter.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Angular bootstrap Date adapter - */ -import { Injectable } from '@angular/core'; -import { NgbDateAdapter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; -import { Moment } from 'moment'; -import * as moment from 'moment'; - -@Injectable() -export class NgbDateMomentAdapter extends NgbDateAdapter { - fromModel(date: Moment): NgbDateStruct { - if (date != null && moment.isMoment(date) && date.isValid()) { - return { year: date.year(), month: date.month() + 1, day: date.date() }; - } - return null; - } - - toModel(date: NgbDateStruct): Moment { - return date ? moment(date.year + '-' + date.month + '-' + date.day, 'YYYY-MM-DD') : null; - } -} diff --git a/src/main/webapp/app/shared/util/linebreaks-pipe.ts b/src/main/webapp/app/shared/util/linebreaks-pipe.ts deleted file mode 100644 index 7cdab263..00000000 --- a/src/main/webapp/app/shared/util/linebreaks-pipe.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; - -@Pipe({ name: 'linebreaks' }) -export class LinebreaksPipe implements PipeTransform { - transform(text: string, as: string = '
'): string { - if (text == null) { - return null; - } - return text.replace(/\n/g, as); - } -} diff --git a/src/main/webapp/app/shared/util/request-util.ts b/src/main/webapp/app/shared/util/request-util.ts deleted file mode 100644 index 6579c3cb..00000000 --- a/src/main/webapp/app/shared/util/request-util.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { HttpParams } from '@angular/common/http'; - -export const createRequestOption = (req?: any): HttpParams => { - let options: HttpParams = new HttpParams(); - if (req) { - Object.keys(req).forEach(key => { - if (key !== 'sort') { - options = options.set(key, req[key]); - } - }); - if (req.sort) { - req.sort.forEach(val => { - options = options.append('sort', val); - }); - } - } - return options; -}; diff --git a/src/main/webapp/app/shared/util/tablefilter.ts b/src/main/webapp/app/shared/util/tablefilter.ts deleted file mode 100644 index f6ac701f..00000000 --- a/src/main/webapp/app/shared/util/tablefilter.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Subject, Subscription } from 'rxjs'; -import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; - -export interface QueryDeclarations { - [key: string]: string; -} -export type DynamicQueryDefinition = (name: string, value: string) => QueryDefinitions; -export interface QueryDefinitions { - [key: string]: string | DynamicQueryDefinition; -} - -/** - * Handles filtering in data tables by converting the user input to query criteria of the JHipster REST API. - * - * It also does not reload during a given debounce period. - */ -export class TableFilter { - criteria: T; - - private criteriaChangedSubject = new Subject(); - private criteriaChangedDebouncer: Subscription; - - constructor(private query: QueryDefinitions, private debounceMillis: number, private reload: () => void) { - this.criteria = {} as any; - this.criteriaChangedDebouncer = this.criteriaChangedSubject.pipe(debounceTime(debounceMillis)).subscribe(() => this.reload()); - } - - trigger($event) { - this.debounce(); - } - - reset() { - this.criteria = {} as any; - this.debounce(); - } - - buildQueryCriteria(): QueryDeclarations { - let queryCriteria: any = {} as any; - Object.keys(this.criteria).forEach(name => { - const value = this.criteria[name]; - if (value === '--') { - queryCriteria[name + '.specified'] = false; - } else if (value === '++') { - queryCriteria[name + '.specified'] = true; - } else { - const queryDef = this.query[name]; - if (typeof queryDef !== 'function') { - queryCriteria[queryDef] = value; - } else { - const additionalQueryCriteria = queryDef(name, value); - queryCriteria = { ...queryCriteria, ...additionalQueryCriteria }; - } - } - }); - return queryCriteria; - } - - private debounce() { - this.criteriaChangedSubject.next(); - } -} - -export function queryYearAsDateRange(name: string, value: string) { - if (value.length === 'YYYY'.length) { - const queryCriteria: any = {} as any; - queryCriteria[name + '.greaterOrEqualThan'] = value + '-01-01'; - queryCriteria[name + '.lessOrEqualThan'] = value + '-12-31'; - return queryCriteria; - } - return null; -} - -export function queryEquals(name: string, value: string) { - return { [name + '.equals']: value }; -} - -export function queryContains(name: string, value: string) { - return { [name + '.contains']: value }; -} diff --git a/src/main/webapp/app/vendor.ts b/src/main/webapp/app/vendor.ts deleted file mode 100644 index 9b608506..00000000 --- a/src/main/webapp/app/vendor.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* after changing this file run 'npm run webpack:build' */ -/* tslint:disable */ -import '../content/css/vendor.css'; - -// Imports all fontawesome core and solid icons - -import { library } from '@fortawesome/fontawesome-svg-core'; -import { - faUser, - faSort, - faSortUp, - faSortDown, - faSync, - faEye, - faBan, - faTimes, - faArrowLeft, - faSave, - faPlus, - faPencilAlt, - faBars, - faThList, - faUserPlus, - faRoad, - faTachometerAlt, - faHeart, - faList, - faBell, - faBook, - faHdd, - faFlag, - faWrench, - faClock, - faCloud, - faSignOutAlt, - faSignInAlt, - faCalendarAlt, - faSearch, - faTrashAlt, - faAsterisk, - faTasks, - faHome -} from '@fortawesome/free-solid-svg-icons'; - -// Adds the SVG icon to the library so you can use it in your page -library.add(faUser); -library.add(faSort); -library.add(faSortUp); -library.add(faSortDown); -library.add(faSync); -library.add(faEye); -library.add(faBan); -library.add(faTimes); -library.add(faArrowLeft); -library.add(faSave); -library.add(faPlus); -library.add(faPencilAlt); -library.add(faBars); -library.add(faHome); -library.add(faThList); -library.add(faUserPlus); -library.add(faRoad); -library.add(faTachometerAlt); -library.add(faHeart); -library.add(faList); -library.add(faBell); -library.add(faTasks); -library.add(faBook); -library.add(faHdd); -library.add(faFlag); -library.add(faWrench); -library.add(faClock); -library.add(faCloud); -library.add(faSignOutAlt); -library.add(faSignInAlt); -library.add(faCalendarAlt); -library.add(faSearch); -library.add(faTrashAlt); -library.add(faAsterisk); - -// jhipster-needle-add-element-to-vendor - JHipster will add new menu items here diff --git a/src/main/webapp/content/css/documentation.css b/src/main/webapp/content/css/documentation.css deleted file mode 100644 index 4aeb51b1..00000000 --- a/src/main/webapp/content/css/documentation.css +++ /dev/null @@ -1,3 +0,0 @@ -/*! - * Your CSS files will be generated in this directory by Webpack - */ diff --git a/src/main/webapp/content/css/global.css b/src/main/webapp/content/css/global.css deleted file mode 100644 index 9c162c66..00000000 --- a/src/main/webapp/content/css/global.css +++ /dev/null @@ -1,238 +0,0 @@ -/* ============================================================== -Bootstrap tweaks -===============================================================*/ - -body, -h1, -h2, -h3, -h4 { - font-weight: 300; -} - -body { - background: #e4e5e6; -} - -a { - color: #533f03; - font-weight: bold; -} - -a:hover { - color: #533f03; - font-weight: bold; - /* make sure browsers use the pointer cursor for anchors, even with no href */ - cursor: pointer; -} - -/* ========================================================================== -Browser Upgrade Prompt -========================================================================== */ -.browserupgrade { - margin: 0.2em 0; - background: #ccc; - color: #000; - padding: 0.2em 0; -} - -/* ========================================================================== -Generic styles -========================================================================== */ - -/* Error highlight on input fields */ -.ng-valid[required], -.ng-valid.required { - border-left: 5px solid green; -} - -.ng-invalid:not(form) { - border-left: 5px solid red; -} - -/* other generic styles */ - -.jh-card { - padding: 1.5%; - margin-top: 20px; - border: none; -} - -.error { - color: white; - background-color: red; -} - -.pad { - padding: 10px; -} - -.w-40 { - width: 40% !important; -} - -.w-60 { - width: 60% !important; -} - -.break { - white-space: normal; - word-break: break-all; -} - -.readonly { - background-color: #eee; - opacity: 1; -} - -.footer { - border-top: 1px solid rgba(0, 0, 0, 0.125); -} - -.hand, -[jhisortby] { - cursor: pointer; -} - -/* ========================================================================== -Custom alerts for notification -========================================================================== */ -.alerts .alert { - text-overflow: ellipsis; -} -.alert pre { - background: none; - border: none; - font: inherit; - color: inherit; - padding: 0; - margin: 0; -} - -.alert .popover pre { - font-size: 10px; -} - -.alerts .toast { - position: fixed; - width: 100%; -} - -.alerts .toast.left { - left: 5px; -} - -.alerts .toast.right { - right: 5px; -} - -.alerts .toast.top { - top: 55px; -} - -.alerts .toast.bottom { - bottom: 55px; -} - -@media screen and (min-width: 480px) { - .alerts .toast { - width: 50%; - } -} - -/* ========================================================================== -entity tables helpers -========================================================================== */ -/* Remove Bootstrap padding from the element -http://stackoverflow.com/questions/19562903/remove-padding-from-columns-in-bootstrap-3 */ -.no-padding-left { - padding-left: 0 !important; -} -.no-padding-right { - padding-right: 0 !important; -} -.no-padding-top { - padding-top: 0 !important; -} -.no-padding-bottom { - padding-bottom: 0 !important; -} -.no-padding { - padding: 0 !important; -} - -/* bootstrap 3 input-group 100% width -http://stackoverflow.com/questions/23436430/bootstrap-3-input-group-100-width */ -.width-min { - width: 1% !important; -} - -/* Makes toolbar not wrap on smaller screens -http://www.sketchingwithcss.com/samplechapter/cheatsheet.html#right */ -.flex-btn-group-container { - display: -webkit-flex; - display: flex; - -webkit-flex-direction: row; - flex-direction: row; - -webkit-justify-content: flex-end; - justify-content: flex-end; -} - -/* ========================================================================== -entity detail page css -========================================================================== */ -.row.jh-entity-details > dd { - margin-bottom: 15px; -} - -@media screen and (min-width: 768px) { - .row.jh-entity-details > dt { - margin-bottom: 15px; - } - - .row.jh-entity-details > dd { - border-bottom: 1px solid #eee; - padding-left: 180px; - margin-left: 0; - } -} - -/* ========================================================================== -ui bootstrap tweaks -========================================================================== */ -.nav, -.pagination, -.carousel, -.card-title a { - cursor: pointer; -} - -.datetime-picker-dropdown > li.date-picker-menu div > table .btn-secondary, -.uib-datepicker-popup > li > div.uib-datepicker > table .btn-secondary { - border: 0; -} - -.datetime-picker-dropdown > li.date-picker-menu div > table:focus, -.uib-datepicker-popup > li > div.uib-datepicker > table:focus { - outline: none; -} - -.thread-dump-modal-lock { - max-width: 450px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* jhipster-needle-css-add-main JHipster will add new css style */ - -/* ========================================================================== -ui tweaks by Hostsharing -========================================================================== */ -.jh-entity-details > dt { - color: lightslategray; -} - -.jh-entity-details > dd { - font-size: 140%; -} diff --git a/src/main/webapp/content/css/loading.css b/src/main/webapp/content/css/loading.css deleted file mode 100644 index a1e24615..00000000 --- a/src/main/webapp/content/css/loading.css +++ /dev/null @@ -1,152 +0,0 @@ -@keyframes lds-pacman-1 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 50% { - -webkit-transform: rotate(-45deg); - transform: rotate(-45deg); - } - 100% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } -} -@-webkit-keyframes lds-pacman-1 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 50% { - -webkit-transform: rotate(-45deg); - transform: rotate(-45deg); - } - 100% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } -} -@keyframes lds-pacman-2 { - 0% { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); - } - 50% { - -webkit-transform: rotate(225deg); - transform: rotate(225deg); - } - 100% { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); - } -} -@-webkit-keyframes lds-pacman-2 { - 0% { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); - } - 50% { - -webkit-transform: rotate(225deg); - transform: rotate(225deg); - } - 100% { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); - } -} -@keyframes lds-pacman-3 { - 0% { - -webkit-transform: translate(190px, 0); - transform: translate(190px, 0); - opacity: 0; - } - 20% { - opacity: 1; - } - 100% { - -webkit-transform: translate(70px, 0); - transform: translate(70px, 0); - opacity: 1; - } -} -@-webkit-keyframes lds-pacman-3 { - 0% { - -webkit-transform: translate(190px, 0); - transform: translate(190px, 0); - opacity: 0; - } - 20% { - opacity: 1; - } - 100% { - -webkit-transform: translate(70px, 0); - transform: translate(70px, 0); - opacity: 1; - } -} - -.app-loading { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; - position: relative; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - top: 10em; -} -.app-loading p { - display: block; - font-size: 1.17em; - margin-inline-start: 0px; - margin-inline-end: 0px; - font-weight: normal; -} - -.app-loading .lds-pacman { - position: relative; - margin: auto; - width: 200px !important; - height: 200px !important; - -webkit-transform: translate(-100px, -100px) scale(1) translate(100px, 100px); - transform: translate(-100px, -100px) scale(1) translate(100px, 100px); -} -.app-loading .lds-pacman > div:nth-child(2) div { - position: absolute; - top: 40px; - left: 40px; - width: 120px; - height: 60px; - border-radius: 120px 120px 0 0; - background: #bbcedd; - -webkit-animation: lds-pacman-1 1s linear infinite; - animation: lds-pacman-1 1s linear infinite; - -webkit-transform-origin: 60px 60px; - transform-origin: 60px 60px; -} -.app-loading .lds-pacman > div:nth-child(2) div:nth-child(2) { - -webkit-animation: lds-pacman-2 1s linear infinite; - animation: lds-pacman-2 1s linear infinite; -} -.app-loading .lds-pacman > div:nth-child(1) div { - position: absolute; - top: 97px; - left: -8px; - width: 24px; - height: 10px; - background-image: url('../images/logo-jhipster.png'); - background-size: contain; - -webkit-animation: lds-pacman-3 1s linear infinite; - animation: lds-pacman-3 1.5s linear infinite; -} -.app-loading .lds-pacman > div:nth-child(1) div:nth-child(1) { - -webkit-animation-delay: -0.67s; - animation-delay: -1s; -} -.app-loading .lds-pacman > div:nth-child(1) div:nth-child(2) { - -webkit-animation-delay: -0.33s; - animation-delay: -0.5s; -} -.app-loading .lds-pacman > div:nth-child(1) div:nth-child(3) { - -webkit-animation-delay: 0s; - animation-delay: 0s; -} diff --git a/src/main/webapp/content/css/vendor.css b/src/main/webapp/content/css/vendor.css deleted file mode 100644 index 7e3ef984..00000000 --- a/src/main/webapp/content/css/vendor.css +++ /dev/null @@ -1,2 +0,0 @@ -/* after changing this file run 'npm run webpack:build' */ -@import '~bootstrap/dist/css/bootstrap.min.css'; diff --git a/src/main/webapp/content/images/jhipster_family_member_0.svg b/src/main/webapp/content/images/jhipster_family_member_0.svg deleted file mode 100755 index 1f9ab527..00000000 --- a/src/main/webapp/content/images/jhipster_family_member_0.svg +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/webapp/content/images/jhipster_family_member_0_head-192.png b/src/main/webapp/content/images/jhipster_family_member_0_head-192.png deleted file mode 100644 index 81330689211e5a082081222dcaf30c37ea6104cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13322 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 E2c#yGumAu6 diff --git a/src/main/webapp/content/images/jhipster_family_member_0_head-256.png b/src/main/webapp/content/images/jhipster_family_member_0_head-256.png deleted file mode 100644 index b4739ec3e47edbc81542888fd0fe7f9bb411c5b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17812 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 diff --git a/src/main/webapp/content/images/jhipster_family_member_1.svg b/src/main/webapp/content/images/jhipster_family_member_1.svg deleted file mode 100755 index 7a118f30..00000000 --- a/src/main/webapp/content/images/jhipster_family_member_1.svg +++ /dev/null @@ -1,9387 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/src/main/webapp/content/images/jhipster_family_member_1_head-192.png b/src/main/webapp/content/images/jhipster_family_member_1_head-192.png deleted file mode 100644 index dd2643c15dd3055d1ca3fc057e0e4f7b0b93b785..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19660 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 diff --git a/src/main/webapp/content/images/jhipster_family_member_1_head-384.png b/src/main/webapp/content/images/jhipster_family_member_1_head-384.png deleted file mode 100644 index f930681c128095fb436ebc8b486e94baf6cfc186..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48895 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 UO0?Ay3<5r~Qi_t*;zl9=2eGOgGynhq diff --git a/src/main/webapp/content/images/jhipster_family_member_1_head-512.png b/src/main/webapp/content/images/jhipster_family_member_1_head-512.png deleted file mode 100644 index 9110dc8fe44bf6ed248a2723f15e900d87cec9a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68803 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

Wv|Q)i4zQT>0V0j?|=lCiv9> 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 l0H^_Ab#1lF#l^+?e*r!59D4!FVj} - - -image/svg+xml \ No newline at end of file diff --git a/src/main/webapp/content/images/jhipster_family_member_2_head-192.png b/src/main/webapp/content/images/jhipster_family_member_2_head-192.png deleted file mode 100644 index 2699ab447391d7a1dfb0fceb9f47e30183a731f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11463 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 diff --git a/src/main/webapp/content/images/jhipster_family_member_2_head-384.png b/src/main/webapp/content/images/jhipster_family_member_2_head-384.png deleted file mode 100644 index da964fcb2e387f81a934f2dc669462aa4a6c85ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23850 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 diff --git a/src/main/webapp/content/images/jhipster_family_member_2_head-512.png b/src/main/webapp/content/images/jhipster_family_member_2_head-512.png deleted file mode 100644 index 6337f5effb4ae653bf2daa4af71df885205dbd72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32300 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/webapp/content/images/jhipster_family_member_3_head-192.png b/src/main/webapp/content/images/jhipster_family_member_3_head-192.png deleted file mode 100644 index 35b91257fcce7a1664efa2979af2fa4ee4afb97a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13573 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 LNkvXXu0mjfv2Czq diff --git a/src/main/webapp/content/images/jhipster_family_member_3_head-256.png b/src/main/webapp/content/images/jhipster_family_member_3_head-256.png deleted file mode 100644 index 098fd8fb05ae1f40a2ad8cef4547c2cae2adda83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19239 zcmV*pKt{ibP)`1aL%Tg?hqDfhW7Da*(MN$$QSOkJ3DhVvw z_UUuWIr(E2Wq10_otXuJ_j&M;d&@cB&VF;scfKMxPeqPyTR2bo)-@W|3iL`eD}hxS zA_4{g0rUe!Ag5s*$O#zNnu8L3P$S;dLLbnkcyZbNhsS#O>zs9nUJEc=IC970s5XuF zBlJ7bTnAhnY!CXNM!W#x1u6K!xRLzPhWmFp!S?a?;ha`XeIN43D~o?hDC6ff`fec6 zwax`ut$6|=9+SfV_TmSQyweK@jLP5e?xFtuVtCyRRs47p}u-vN#BNw zdEggThQn#KT4}FeYcU`N18AWX+L1zdS}O(eH-Kw;Eo(KD)<027|GncD{%Y;RW2s)t z(+kdO#nd-r4h<~10_0Cg^fe0B30MipsxJw4qG!!0Sre@_j^~XO3eGdW@*bC3KfK}b z%&uO`)eX)%#nks9hhAKAixl$Yx-i*f3$KH?s3XF45XW`N7YZm}9aLIBqzv(wmp+z$ zqt|kEinB&B5B$Q)u$}V%73g0>v#RBWENcSS?GI42uHrZj`9cw2DXrnVX!WO?cVr*! zwOm0n>lIVqhdlDqvR@GBKhUtK-S&hXMmvQdMC%<|Q*cTnsMZ<~k80olcblKczS3*a zf?`%H=CQk%ZS>H8BjAdl+7zaV_Pn5)>nTbp@`VCL#|7|&=1&Ug^zUwc9=F%Bwactj zObz15Jxf1>=Dz{qKpPhVv<|e%UgLNk*<2p26u@)7HDNJ8MMZeLceRn_RRJ!b=@{SSIonAFZ(Nj{#38!I1>%Hu8Zq> zxUL66V;Ba8G?0eGloBZor0n^}R~OIoNM&+>9z)10H*KHX*K3&?p{Jkhk-L|ESi_fl zDZ^g2*(Kj9X(b`nEF

+u_yv@`A>EvKGth_YSGo< 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 DEYbKo diff --git a/src/main/webapp/favicon.ico b/src/main/webapp/favicon.ico deleted file mode 100755 index 4179874f53b3c3b0ba9e2a401412f814ab6296bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1574 zcmV+>2HE+EP)000H;Nkl9r4a2J+cS@yzSX6E!^*k!$-D-4;C?DsI4Z2te8^PT@c z=RfD)VMK(b3{GnU7K#)Bt&t+2HBtn$Mv8#eNDUS>&~)z8>N1c(q8rNd{sPjn1MM2QHDA^sG2W;HKsEVpi{%G+8~m}54A zpq~8zXet!#9Gbj-*V8>RHQ@_Oa=ck5fDynmyijTRbXS$xsGFssxLtW3`my8G-!>%7 zn;n<%&U37xG-qc+$&NL}Ic8(r8P8{L>@pz`5wF~FxA(hl3{Q#@0mJ|T!>orWW&$y= zwZ)lV?q9=mOj&=001@Fo=j2<5|CsUovve~87<4?ht)^g4)54xHIM_fH6g9vTH~{$6e3n@@!>>0A((rb8tLK5s#V0 zMm+wn&))p*T>q|hCGR#@SL9}ZK#Tw|V#*5$4}y`?UCz_p4!1wNkO1l#@e&9G#+VYs zEG&vW!|wTsW3iPFM8p#vgQq%eFRm{7prxk1*aAS&G~ti@^w-GQ9%mpd0W^=8Nrc@q z?FmG(O?n~{0D#CgKIQg@lG#5`2LTaLZtu09`*&npbwOKeU4B;jv8(n!ddI?|{BT~F zzml*h^*el9D=mnp(SK|%RkC6{JP5dS>;C22j@;ZLQI=y-twP>a1a%F2w^mvhW1N!C zItRzq%;^AP-WFw5y#6R|j(8Qh9Dt}K_u4&+p(d6Y7r5u2fMIuVYB~yqz^KPR<_)T> zVUZCQ2K>q>IWj4>kGx1p%HD(9OEwu=KUlGU-EF%U2| z6%r-`VTl%YwkK@x(j4)7%q9E||Yz*Vu z-KgXDY^wP1l~c9KAAp;kIku<%ZR4V3H)gcci^L zxuYQ7iwPXJy}sz+_S%}ltQn(|jw6aU+5iCCMBw-}`=x=2s3a#J7eur?P5(n%lfW3; zzojxs0t_(d_}%ME4>VV=%*&kl@mY?4RLIPDrr1%QWBTmX>U-{zUphzI`^LkPoTQkY z^?7=MW3nvEM4$hB{Zyw7M3hi@DgLIIc}3Z#J)0`_Hm$Unjj=qofc>keP`dJ1mj{ks?4R(3kPwGF$MQ4Nw$&8u zY$#b@Zsp>+MfD;l;gbiMsB74J{+6r5=JEI=N`S;Tl0o2i)aJImRADmkV6kfzMM5Yl zb`4GR+C9Ed)T9?ySkhM)WtCdZJg310p5oRacks5uH}YWG9~KQdzS3&iP?nXGu8&`< zB@h73l>ryEsGJ*5{SM`E0!tK2{&F`(Kx?E3XpIyBt&t+2HBtn$Mv8#eNDIhr Benutzer wurde aktiviert. Bitte ", - "error": "Ihr Benutzer konnte nicht aktiviert werden. Bitte benutzen Sie die Registrierungsmaske, um sich zu registrieren." - } - } -} diff --git a/src/main/webapp/i18n/de/asset.json b/src/main/webapp/i18n/de/asset.json deleted file mode 100644 index dee8fe54..00000000 --- a/src/main/webapp/i18n/de/asset.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "hsadminNgApp": { - "asset": { - "home": { - "title": "Geschäftsguthaben-Transaktionen", - "createLabel": "Geschäftsguthaben-Transaktion erfassen", - "createOrEditLabel": "Geschäftsguthaben-Transaktion erfassen oder bearbeiten" - }, - "created": "Geschäftsguthaben-Transaktion erfasst mit ID {{ param }}", - "updated": "Geschäftsguthaben-Transaktion aktualisiert mit ID {{ param }}", - "deleted": "Geschäftsguthaben-Transaktion gelöscht mit ID {{ param }}", - "delete": { - "question": "Soll die Geschäftsguthaben-Transaktion {{ id }} wirklich dauerhaft gelöscht werden?" - }, - "detail": { - "title": "Geschäftsguthaben-Transaktion" - }, - "documentDate": "Belegdatum", - "valueDate": "Buchungsdatum", - "action": "Transaktion", - "amount": "Betrag", - "remark": "Bemerkung", - "membership": "zugehörige Mitgliedschaft" - } - } -} diff --git a/src/main/webapp/i18n/de/assetAction.json b/src/main/webapp/i18n/de/assetAction.json deleted file mode 100644 index e89d58f3..00000000 --- a/src/main/webapp/i18n/de/assetAction.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "hsadminNgApp": { - "AssetAction": { - "null": "", - "PAYMENT": "Einzahlung", - "HANDOVER": "Übertragung", - "ADOPTION": "Übernahme", - "LOSS": "Verlust", - "CLEARING": "Verrechnung", - "PAYBACK": "Auszahlung" - } - } -} diff --git a/src/main/webapp/i18n/de/audits.json b/src/main/webapp/i18n/de/audits.json deleted file mode 100644 index 28b324cf..00000000 --- a/src/main/webapp/i18n/de/audits.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "audits": { - "title": "Audits", - "filter": { - "title": "Nach Datum filtern", - "from": "von", - "to": "bis", - "button": { - "weeks": "Wochen", - "today": "heute", - "clear": "leeren", - "close": "abschließen" - } - }, - "table": { - "header": { - "principal": "Benutzer", - "date": "Datum", - "status": "Stand", - "data": "Extra Informationen" - }, - "data": { - "remoteAddress": "Remote-Adresse:" - } - } - } -} diff --git a/src/main/webapp/i18n/de/configuration.json b/src/main/webapp/i18n/de/configuration.json deleted file mode 100644 index 6395139c..00000000 --- a/src/main/webapp/i18n/de/configuration.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "configuration": { - "title": "Konfiguration", - "filter": "Filter (nach Präfix)", - "table": { - "prefix": "Präfix", - "properties": "Eigenschaften" - } - } -} diff --git a/src/main/webapp/i18n/de/custom-error.json b/src/main/webapp/i18n/de/custom-error.json deleted file mode 100644 index b62e6f9c..00000000 --- a/src/main/webapp/i18n/de/custom-error.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "error": { - "idNotFound": "Technische Datensatz-ID nicht gefunden", - "unknownProperty": "Unbekannte Eigenschaft", - "shareSubscriptionPositiveQuantity": "Zeichnungen von Geschäftsanteilen erfordern eine positive Stückzahl", - "shareCancellationNegativeQuantity": "Kündigungen von Geschäftsanteilen erfordern eine negative Stückzahl", - "shareTransactionImmutable": "Transaktionen mit Geschäftsanteilen sind unveränderlich", - "membershipNotDeletable": "Mitgliedschaft kann nicht gelöscht werden, setze stattdessen das 'untilDate'", - "customerNotDeletable": "Kunden können nicht explizit gelöscht werden'", - "untilDateMustBeAfterSinceDate": "Mitgliedshafts-Austrittsdatum muss nach dem Beitrittsdatum liegen", - "anotherUncancelledMembershipExists": "Nur eine einzige ungekündigte Mitgliedschaft pro Kunde ist zulässig", - "initializationProhibited": "Initialisierung des Feldes unzulässig", - "updateProhibited": "Aktualisierung des Feldes unzulässig", - "documentDateMayNotBeAfterValueDate": "Belegdatum darf nicht vor dem Buchungsdatum liegen", - "assetTransactionImmutable": "Transaktionen mit Geschäftsguthaben sind unveränderlich", - "assetPaymentsPositiveAmount": "Einzahlungen von Geschäftsguthaben erfordern einen positiven Betrag", - "assetAdoptionsPositiveAmount": "Übernahmen von Geschäftsguthaben erfordern einen positiven Betrag", - "assetPaybacksNegativeAmount": "Auszahlungen von Geschäftsguthaben erfordern einen negativen Betrag", - "assetHandoversNegativeAmount": "Übertragungen von Geschäftsguthaben erfordern einen negativen Betrag", - "assetLossesNegativeAmount": "Verluste von Geschäftsguthaben erfordern einen negativen Betrag", - "assetClearingsNegativeAmount": "Verrechnungen von Geschäftsguthaben erfordern einen negativen Betrag" - } -} diff --git a/src/main/webapp/i18n/de/customer.json b/src/main/webapp/i18n/de/customer.json deleted file mode 100644 index af18676c..00000000 --- a/src/main/webapp/i18n/de/customer.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "hsadminNgApp": { - "customer": { - "home": { - "title": "Customers", - "createLabel": "Customer erstellen", - "createOrEditLabel": "Customer erstellen oder bearbeiten" - }, - "created": "Customer erstellt mit ID {{ param }}", - "updated": "Customer aktualisiert mit ID {{ param }}", - "deleted": "Customer gelöscht mit ID {{ param }}", - "delete": { - "question": "Soll Customer {{ id }} wirklich dauerhaft gelöscht werden?" - }, - "detail": { - "title": "Customer" - }, - "reference": "Reference", - "prefix": "Prefix", - "name": "Name", - "kind": "Kind", - "birthDate": "Birth Date", - "birthPlace": "Birth Place", - "registrationCourt": "Registration Court", - "registrationNumber": "Registration Number", - "vatRegion": "Vat Region", - "vatNumber": "Vat Number", - "contractualSalutation": "Contractual Salutation", - "contractualAddress": "Contractual Address", - "billingSalutation": "Billing Salutation", - "billingAddress": "Billing Address", - "remark": "Remark", - "membership": "Membership", - "sepamandate": "Sepamandate" - } - } -} diff --git a/src/main/webapp/i18n/de/customerKind.json b/src/main/webapp/i18n/de/customerKind.json deleted file mode 100644 index 62f07a45..00000000 --- a/src/main/webapp/i18n/de/customerKind.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "hsadminNgApp": { - "CustomerKind": { - "null": "", - "NATURAL": "natürliche Person", - "LEGAL": "juristische Person" - } - } -} diff --git a/src/main/webapp/i18n/de/error.json b/src/main/webapp/i18n/de/error.json deleted file mode 100644 index f68d3c70..00000000 --- a/src/main/webapp/i18n/de/error.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "error": { - "title": "Fehlerseite!", - "http": { - "400": "Ungültige Anfrage.", - "403": "Sie haben nicht die nötigen Berechtigungen um diese Seite anzuzeigen.", - "404": "Die Seite existiert nicht.", - "405": "Die verwendete Anfrage-Methode ist für diese URL nicht erlaubt.", - "500": "Interner Serverfehler." - }, - "concurrencyFailure": "Ein anderer Benutzer hat diese Daten zeitgleich mit Ihnen geändert. Ihre Änderungen wurden abgelehnt.", - "validation": "Validierungsfehler auf dem Server." - } -} diff --git a/src/main/webapp/i18n/de/global.json b/src/main/webapp/i18n/de/global.json deleted file mode 100644 index fabdfc34..00000000 --- a/src/main/webapp/i18n/de/global.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "global": { - "title": "HsadminNg", - "browsehappy": "Sie benutzen einen veralteten Browser. Bitte aktualisieren Sie Ihren Browser, um die Benutzerfreundlichkeit zu erhöhen.", - "menu": { - "home": "Startseite", - "jhipster-needle-menu-add-element": "JHipster will add additional menu entries here (do not translate!)", - "entities": { - "main": "Entitäten", - "customer": "Customer", - "membership": "Membership", - "share": "Share", - "asset": "Asset", - "sepaMandate": "Sepa Mandate", - "userRoleAssignment": "User Role Assignment", - "jhipster-needle-menu-add-entry": "JHipster will add additional entities here (do not translate!)" - }, - "account": { - "main": "Zugang", - "settings": "Einstellungen", - "password": "Passwort", - "sessions": "Sitzungen", - "login": "Anmelden", - "logout": "Abmelden", - "register": "Registrierung" - }, - "admin": { - "main": "Administration", - "userManagement": "Benutzerverwaltung", - "tracker": "Benutzer Aktivitäten", - "metrics": "Metriken", - "health": "Status", - "configuration": "Konfiguration", - "logs": "Protokoll", - "audits": "Audits", - "apidocs": "API", - "database": "Datenbank", - "jhipster-needle-menu-add-admin-element": "JHipster will add additional menu entries here (do not translate!)" - }, - "language": "Sprache" - }, - "form": { - "username": "Benutzername", - "username.placeholder": "Ihr Benutzername", - "currentpassword": "Aktuelles Passwort", - "currentpassword.placeholder": "Aktuelles Passwort", - "newpassword": "Neues Passwort", - "newpassword.placeholder": "Neues Passwort", - "confirmpassword": "Neues Passwort bestätigen", - "confirmpassword.placeholder": "Bestätigen Sie Ihr neues Passwort", - "email": "Email Adresse", - "email.placeholder": "Ihre Email Adresse" - }, - "messages": { - "info": { - "authenticated": { - "prefix": "Wenn Sie sich ", - "link": "anmelden", - "suffix": " möchten, versuchen Sie es mit
- Administrator (Name=\"admin\" und Passwort=\"admin\")
- Benutzer (Name=\"user\" und Passwort=\"user\")." - }, - "register": { - "noaccount": "Sie haben noch keinen Zugang?", - "link": "Registrieren Sie sich" - } - }, - "error": { - "dontmatch": "Das bestätigte Passwort entspricht nicht dem neuen Passwort!" - }, - "validate": { - "newpassword": { - "required": "Ein neues Passwort wird benötigt.", - "minlength": "Das neue Passwort muss mindestens 5 Zeichen lang sein", - "maxlength": "Das neue Passwort darf nicht länger als 50 Zeichen sein", - "strength": "Passwortstärke:" - }, - "confirmpassword": { - "required": "Sie müssen das Passwort bestätigen.", - "minlength": "Das bestätigte Passwort muss mindestens 5 Zeichen lang sein", - "maxlength": "Das bestätigte Passwort darf nicht länger als 50 Zeichen sein" - }, - "email": { - "required": "Ihre Email Adresse wird benötigt.", - "invalid": "Ihre Email Adresse ist ungültig.", - "minlength": "Ihre Email Adresse muss mindestens 5 Zeichen lang sein", - "maxlength": "Ihre Email Adresse darf nicht länger als 50 Zeichen sein" - } - } - }, - "field": { - "id": "ID" - }, - "ribbon": { - "dev": "Development" - }, - "item-count": "Ergebnis {{first}} - {{second}} von {{total}} Elemente." - }, - "entity": { - "action": { - "addblob": "Blob hinzufügen", - "addimage": "Bild hinzufügen", - "back": "Zurück", - "cancel": "Abbrechen", - "delete": "Löschen", - "edit": "Bearbeiten", - "open": "Öffnen", - "save": "Speichern", - "view": "Details" - }, - "detail": { - "field": "Feld", - "value": "Wert" - }, - "delete": { - "title": "Löschen bestätigen" - }, - "validation": { - "required": "Dieses Feld wird benötigt.", - "minlength": "Dieses Feld muss mind. {{min}} Zeichen lang sein.", - "maxlength": "Dieses Feld darf max. {{max}} Zeichen lang sein.", - "min": "Dieses Feld muss größer als {{min}} sein.", - "max": "Dieses Feld muss kleiner als {{max}} sein.", - "minbytes": "Dieses Feld sollte mehr als {{min}} bytes haben.", - "maxbytes": "Dieses Feld sollte nicht mehr als {{max}} bytes haben.", - "pattern": "Dieses Feld muss das Muster {{pattern}} erfüllen.", - "number": "Dieses Feld muss eine Zahl sein.", - "datetimelocal": "Dieses Feld muss eine Datums- und Zeitangabe enthalten.", - "duplicate": "Ein Wert ist doppelt zu existierenden Daten." - } - }, - "error": { - "internalServerError": "Interner Serverfehler", - "server.not.reachable": "Server ist nicht erreichbar", - "url.not.found": "Nicht gefunden", - "NotNull": "Feld {{fieldName}} kann nicht leer sein!", - "Size": "Feld {{fieldName}} erfüllt nicht die minimalen/maximalen Voraussetzungen!", - "userexists": "Benutzername bereits vergeben!", - "emailexists": "Email wird bereits verwendet!", - "idexists": "Ein neuer {{entityName}} kann noch keine ID haben", - "idnull": "Ungültige ID" - }, - "footer": "Dies ist Ihre Fußzeile" -} diff --git a/src/main/webapp/i18n/de/health.json b/src/main/webapp/i18n/de/health.json deleted file mode 100644 index e23e6ccc..00000000 --- a/src/main/webapp/i18n/de/health.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "health": { - "title": "Gesundheit Ihrer Anwendung", - "refresh.button": "Aktualisieren", - "stacktrace": "Stacktrace", - "details": { - "details": "Details", - "properties": "Properties", - "name": "Name", - "value": "Wert", - "error": "Error" - }, - "indicator": { - "diskSpace": "Festplattenspeicher", - "mail": "Email", - "db": "Datenbank" - }, - "table": { - "service": "Dienst Name", - "status": "Status" - }, - "status": { - "UNKNOWN": "UNKNOWN", - "UP": "UP", - "DOWN": "DOWN" - } - } -} diff --git a/src/main/webapp/i18n/de/home.json b/src/main/webapp/i18n/de/home.json deleted file mode 100644 index 57f16478..00000000 --- a/src/main/webapp/i18n/de/home.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "home": { - "title": "Willkommen, Java Hipster!", - "subtitle": "Dies ist Ihre Hauptseite", - "logged": { - "message": "Sie sind als Benutzer \"{{username}}\" angemeldet." - }, - "question": "Wenn Sie Fragen zu JHipster haben:", - "link": { - "homepage": "JHipster Hauptseite", - "stackoverflow": "JHipster auf Stack Overflow", - "bugtracker": "JHipster Fehlereinträge", - "chat": "JHipster Chat", - "follow": "kontaktieren Sie uns mit @java_hipster auf Twitter" - }, - "like": "Wenn Sie JHipster mögen, vergessen Sie nicht uns einen Stern zu geben auf", - "github": "GitHub" - } -} diff --git a/src/main/webapp/i18n/de/login.json b/src/main/webapp/i18n/de/login.json deleted file mode 100644 index cf7b2186..00000000 --- a/src/main/webapp/i18n/de/login.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "login": { - "title": "Anmeldung", - "form": { - "password": "Passwort", - "password.placeholder": "Ihr Passwort", - "rememberme": "Automatische Anmeldung", - "button": "Anmelden" - }, - "messages": { - "error": { - "authentication": "Anmeldung fehlgeschlagen! Überprüfen Sie bitte Ihre Angaben und versuchen Sie es erneut." - } - }, - "password": { - "forgot": "Sie haben Ihr Passwort vergessen?" - } - } -} diff --git a/src/main/webapp/i18n/de/logs.json b/src/main/webapp/i18n/de/logs.json deleted file mode 100644 index 9416c965..00000000 --- a/src/main/webapp/i18n/de/logs.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "logs": { - "title": "Protokolle", - "nbloggers": "Es existieren {{ total }} Logger.", - "filter": "Filter", - "table": { - "name": "Name", - "level": "Stufe" - } - } -} diff --git a/src/main/webapp/i18n/de/membership.json b/src/main/webapp/i18n/de/membership.json deleted file mode 100644 index e277b4dc..00000000 --- a/src/main/webapp/i18n/de/membership.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "hsadminNgApp": { - "membership": { - "home": { - "title": "Memberships", - "createLabel": "Membership erstellen", - "createOrEditLabel": "Membership erstellen oder bearbeiten" - }, - "created": "Membership erstellt mit ID {{ param }}", - "updated": "Membership aktualisiert mit ID {{ param }}", - "deleted": "Membership gelöscht mit ID {{ param }}", - "delete": { - "question": "Soll Membership {{ id }} wirklich dauerhaft gelöscht werden?" - }, - "detail": { - "title": "Membership" - }, - "admissionDocumentDate": "Admission Document Date", - "cancellationDocumentDate": "Cancellation Document Date", - "memberFromDate": "Member From Date", - "memberUntilDate": "Member Until Date", - "remark": "Remark", - "share": "Share", - "asset": "Asset", - "customer": "Customer" - } - } -} diff --git a/src/main/webapp/i18n/de/metrics.json b/src/main/webapp/i18n/de/metrics.json deleted file mode 100644 index 63e36ecb..00000000 --- a/src/main/webapp/i18n/de/metrics.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "metrics": { - "title": "Anwendungs-Metriken", - "refresh.button": "Aktualisieren", - "updating": "Aktualisierung...", - "jvm": { - "title": "JVM Metriken", - "memory": { - "title": "Hauptspeicher", - "total": "Gesamter Hauptspeicher", - "heap": "Heap Speicher", - "nonheap": "Non-Heap Speicher" - }, - "threads": { - "title": "Threads", - "all": "Alle", - "runnable": "Runnable", - "timedwaiting": "Wartezeit", - "waiting": "Wartend", - "blocked": "Geblockt", - "dump": { - "title": "Threads dump", - "id": "Id: ", - "blockedtime": "Geblockte Zeit", - "blockedcount": "Geblockte Zahl", - "waitedtime": "Gewartete Zeit", - "waitedcount": "Gewartete Anzahl", - "lockname": "Lock-Name", - "stacktrace": "Stacktrace", - "show": "Zeigen", - "hide": "Verstecken" - } - }, - "gc": { - "title": "Speicherbereinigung (GC)", - "marksweepcount": "Durchlaufmarkierungs (Mark Sweep) Anzahl", - "marksweeptime": "Durchlaufmarkierungs (Mark Sweep) Zeit", - "scavengecount": "Bereinigungslauf (Scavenge) Anzahl", - "scavengetime": "Bereinigungslauf (Scavenge) Zeit" - }, - "http": { - "title": "HTTP Anfragen (Ereignisse pro Sekunde)", - "active": "Aktive Anfragen:", - "total": "Alle Anfragen:", - "table": { - "code": "Codierung", - "count": "Anzahl", - "mean": "Durchschnittswert", - "average": "Mittelwert" - }, - "code": { - "ok": "Ok", - "notfound": "Nicht gefunden", - "servererror": "Server Fehler" - } - } - }, - "servicesstats": { - "title": "Service Statistiken (Zeit in Millisekunden)", - "table": { - "name": "Service Name", - "count": "Anzahl", - "mean": "Durchschnitt", - "min": "Minimum", - "max": "Maximum", - "p50": "p50", - "p75": "p75", - "p95": "p95", - "p99": "p99" - } - }, - "cache": { - "title": "Cache Statistiken", - "cachename": "Cache Name", - "hits": "Treffer", - "misses": "Keine Treffer", - "evictions": "Anzahl entfernter Objekte" - }, - "datasource": { - "usage": "Usage", - "title": "Datenquelle (Zeit in Millisekunden)", - "name": "Pool-Auslastung", - "count": "Anzahl", - "mean": "Mittel", - "min": "Min", - "max": "Max", - "p50": "p50", - "p75": "p75", - "p95": "p95", - "p99": "p99" - } - } -} diff --git a/src/main/webapp/i18n/de/password.json b/src/main/webapp/i18n/de/password.json deleted file mode 100644 index 3be321d7..00000000 --- a/src/main/webapp/i18n/de/password.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "password": { - "title": "Passwort für [{{username}}]", - "form": { - "button": "Speichern" - }, - "messages": { - "error": "Es ist ein Fehler aufgetreten! Das Passwort konnte nicht geändert werden.", - "success": "Passwort wurde geändert!" - } - } -} diff --git a/src/main/webapp/i18n/de/register.json b/src/main/webapp/i18n/de/register.json deleted file mode 100644 index 858c6fa5..00000000 --- a/src/main/webapp/i18n/de/register.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "register": { - "title": "Registrierung", - "form": { - "button": "Registrieren" - }, - "messages": { - "validate": { - "login": { - "required": "Ihr Benutzername wird benötigt.", - "minlength": "Ihr Benutzername muss mindestens 1 Zeichen lang sein", - "maxlength": "Ihr Benutzername darf nicht länger als 50 Zeichen sein", - "pattern": "Ihr Benutzername darf nur Kleinbuchstaben und Ziffern enthalten" - } - }, - "success": "Registrierung gespeichert! Bitte überprüfen Sie Ihre Emails für die Bestätigung.", - "error": { - "fail": "Registrierung fehlgeschlagen! Bitte versuchen Sie es später nochmal.", - "userexists": "Benutzername bereits vergeben! Bitte wählen Sie einen anderen aus.", - "emailexists": "Email wird bereits verwendet! Bitte wählen Sie eine andere aus." - } - } - } -} diff --git a/src/main/webapp/i18n/de/reset.json b/src/main/webapp/i18n/de/reset.json deleted file mode 100644 index 3f0f6993..00000000 --- a/src/main/webapp/i18n/de/reset.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "reset": { - "request": { - "title": "Passwort zurücksetzen", - "form": { - "button": "Passwort zurücksetzen" - }, - "messages": { - "info": "Geben Sie die Email Adresse ein, welche Sie bei der Registrierung verwendet haben.", - "success": "Eine Email mit weiteren Instruktionen für das Zurücksetzen des Passworts wurde gesendet.", - "notfound": "Diese Email Adresse existiert nicht! Überprüfen Sie Ihre Email Adresse und versuchen Sie es nochmal." - } - }, - "finish": { - "title": "Passwort zurücksetzen", - "form": { - "button": "Neues Passwort setzen" - }, - "messages": { - "info": "Wählen Sie ein neues Passwort", - "success": "Ihr Passwort wurde zurückgesetzt. Bitte ", - "keymissing": "Der Schlüssel zum Zurücksetzen fehlt.", - "error": "Ihr Passwort konnte nicht zurückgesetzt werden. Zur Erinnerung, Ihre Anfrage ist nur 24 Stunden gültig." - } - } - } -} diff --git a/src/main/webapp/i18n/de/sepaMandate.json b/src/main/webapp/i18n/de/sepaMandate.json deleted file mode 100644 index f5ea6b73..00000000 --- a/src/main/webapp/i18n/de/sepaMandate.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "hsadminNgApp": { - "sepaMandate": { - "home": { - "title": "Sepa Mandates", - "createLabel": "Sepa Mandate erstellen", - "createOrEditLabel": "Sepa Mandate erstellen oder bearbeiten" - }, - "created": "Sepa Mandate erstellt mit ID {{ param }}", - "updated": "Sepa Mandate aktualisiert mit ID {{ param }}", - "deleted": "Sepa Mandate gelöscht mit ID {{ param }}", - "delete": { - "question": "Soll Sepa Mandate {{ id }} wirklich dauerhaft gelöscht werden?" - }, - "detail": { - "title": "Sepa Mandate" - }, - "reference": "Reference", - "iban": "Iban", - "bic": "Bic", - "grantingDocumentDate": "Granting Document Date", - "revokationDocumentDate": "Revokation Document Date", - "validFromDate": "Valid From Date", - "validUntilDate": "Valid Until Date", - "lastUsedDate": "Last Used Date", - "remark": "Remark", - "customer": "Customer" - } - } -} diff --git a/src/main/webapp/i18n/de/sessions.json b/src/main/webapp/i18n/de/sessions.json deleted file mode 100644 index d8f61b5f..00000000 --- a/src/main/webapp/i18n/de/sessions.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "sessions": { - "title": "Aktive Sitzungen für [{{username}}]", - "table": { - "ipaddress": "IP Adresse", - "useragent": "User Agent", - "date": "Datum", - "button": "Schließen" - }, - "messages": { - "success": "Sitzung wurde geschlossen!", - "error": "Es ist ein Fehler aufgetreten! Die Sitzung konnte nicht geschlossen werden." - } - } -} diff --git a/src/main/webapp/i18n/de/settings.json b/src/main/webapp/i18n/de/settings.json deleted file mode 100644 index 379065e6..00000000 --- a/src/main/webapp/i18n/de/settings.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "settings": { - "title": "Einstellungen für Benutzer [{{username}}]", - "form": { - "firstname": "Vorname", - "firstname.placeholder": "Ihr Vorname", - "lastname": "Nachname", - "lastname.placeholder": "Ihr Nachname", - "language": "Sprache", - "button": "Speichern" - }, - "messages": { - "error": { - "fail": "Es ist ein Fehler aufgetreten! Die Einstellungen konnten nicht gespeichert werden.", - "emailexists": "Email wird bereits verwendet! Bitte wählen Sie eine andere aus." - }, - "success": "Einstellungen wurden gespeichert!", - "validate": { - "firstname": { - "required": "Ihr Vorname wird benötigt.", - "minlength": "Ihr Vorname muss mindestens 1 Zeichen lang sein", - "maxlength": "Ihr Vorname darf nicht länger als 50 Zeichen sein" - }, - "lastname": { - "required": "Ihr Nachname wird benötigt.", - "minlength": "Ihr Nachname muss mindestens 1 Zeichen lang sein", - "maxlength": "Ihr Nachname darf nicht länger als 50 Zeichen sein" - } - } - } - } -} diff --git a/src/main/webapp/i18n/de/share.json b/src/main/webapp/i18n/de/share.json deleted file mode 100644 index 2cadbe83..00000000 --- a/src/main/webapp/i18n/de/share.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "hsadminNgApp": { - "share": { - "home": { - "title": "Geschäftsanteil-Transaktionen", - "createLabel": "Geschäftsanteil-Transaktion erfassen", - "createOrEditLabel": "Geschäftsanteil-Transaktion erfassen oder bearbeiten" - }, - "created": "Geschäftsanteil-Transaktion erfasst mit ID {{ param }}", - "updated": "Geschäftsanteil-Transaktion aktualisiert mit ID {{ param }}", - "deleted": "Geschäftsanteil-Transaktion gelöscht mit ID {{ param }}", - "delete": { - "question": "Soll die Geschäftsanteil-Transaktion {{ id }} wirklich dauerhaft gelöscht werden?" - }, - "detail": { - "title": "Geschäftsanteil-Transaktion" - }, - "documentDate": "Belegdatum", - "valueDate": "Buchungsdatum", - "action": "Aktion", - "quantity": "Anzahl", - "remark": "Bemerkung", - "membership": "zugehörige Mitgliedschaft" - } - } -} diff --git a/src/main/webapp/i18n/de/shareAction.json b/src/main/webapp/i18n/de/shareAction.json deleted file mode 100644 index 63d4be57..00000000 --- a/src/main/webapp/i18n/de/shareAction.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "hsadminNgApp": { - "ShareAction": { - "null": "", - "SUBSCRIPTION": "Zeichnung", - "CANCELLATION": "Kündigung" - } - } -} diff --git a/src/main/webapp/i18n/de/user-management.json b/src/main/webapp/i18n/de/user-management.json deleted file mode 100644 index b697fedb..00000000 --- a/src/main/webapp/i18n/de/user-management.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "userManagement": { - "home": { - "title": "Benutzer", - "createLabel": "Neuen Benutzer erstellen", - "createOrEditLabel": "Benutzer erstellen oder bearbeiten" - }, - "created": "Ein neuer Benutzer wurde mit ID {{ param }} erstellt", - "updated": "Ein Benutzer mit ID {{ param }} wurde geändert", - "deleted": "Ein Benutzer mit ID {{ param }} wurde gelöscht", - "delete": { - "question": "Sind Sie sicher, dass Sie den Benutzer {{ login }} löschen möchten?" - }, - "detail": { - "title": "Benutzer" - }, - "login": "Login", - "firstName": "Vorname", - "lastName": "Nachname", - "email": "Email", - "activated": "Aktiv", - "deactivated": "Deaktiviert", - "profiles": "Profile", - "langKey": "Sprache", - "createdBy": "Erstellt von", - "createdDate": "Erstellt am", - "lastModifiedBy": "Bearbeitet von", - "lastModifiedDate": "Zuletzt bearbeitet" - } -} diff --git a/src/main/webapp/i18n/de/userRole.json b/src/main/webapp/i18n/de/userRole.json deleted file mode 100644 index 610dc2a4..00000000 --- a/src/main/webapp/i18n/de/userRole.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "hsadminNgApp": { - "UserRole": { - "null": "", - "HOSTMASTER": "HOSTMASTER", - "ADMIN": "ADMIN", - "SUPPORTER": "SUPPORTER", - "CONTRACTUAL_CONTACT": "CONTRACTUAL_CONTACT", - "FINANCIAL_CONTACT": "FINANCIAL_CONTACT", - "TECHNICAL_CONTACT": "TECHNICAL_CONTACT", - "CUSTOMER_USER": "CUSTOMER_USER" - } - } -} diff --git a/src/main/webapp/i18n/de/userRoleAssignment.json b/src/main/webapp/i18n/de/userRoleAssignment.json deleted file mode 100644 index c7c53d20..00000000 --- a/src/main/webapp/i18n/de/userRoleAssignment.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "hsadminNgApp": { - "userRoleAssignment": { - "home": { - "title": "User Role Assignments", - "createLabel": "User Role Assignment erstellen", - "createOrEditLabel": "User Role Assignment erstellen oder bearbeiten" - }, - "created": "User Role Assignment erstellt mit ID {{ param }}", - "updated": "User Role Assignment aktualisiert mit ID {{ param }}", - "deleted": "User Role Assignment gelöscht mit ID {{ param }}", - "delete": { - "question": "Soll User Role Assignment {{ id }} wirklich dauerhaft gelöscht werden?" - }, - "detail": { - "title": "User Role Assignment" - }, - "entityTypeId": "Entity Type Id", - "entityObjectId": "Entity Object Id", - "assignedRole": "Assigned Role", - "user": "User" - } - } -} diff --git a/src/main/webapp/i18n/de/vatRegion.json b/src/main/webapp/i18n/de/vatRegion.json deleted file mode 100644 index 339febc1..00000000 --- a/src/main/webapp/i18n/de/vatRegion.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "hsadminNgApp": { - "VatRegion": { - "null": "", - "DOMESTIC": "DOMESTIC", - "EU": "EU", - "OTHER": "OTHER" - } - } -} diff --git a/src/main/webapp/i18n/en/activate.json b/src/main/webapp/i18n/en/activate.json deleted file mode 100644 index 2926b789..00000000 --- a/src/main/webapp/i18n/en/activate.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "activate": { - "title": "Activation", - "messages": { - "success": "Your user account has been activated. Please ", - "error": "Your user could not be activated. Please use the registration form to sign up." - } - } -} diff --git a/src/main/webapp/i18n/en/asset.json b/src/main/webapp/i18n/en/asset.json deleted file mode 100644 index a380f70f..00000000 --- a/src/main/webapp/i18n/en/asset.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "hsadminNgApp": { - "asset": { - "home": { - "title": "Asset Transactions", - "createLabel": "Register a new asset transaction", - "createOrEditLabel": "Register or edit an asset transaction" - }, - "created": "A new asset transaction is registered with identifier {{ param }}", - "updated": "An asset transaction is updated with identifier {{ param }}", - "deleted": "An asset transaction is deleted with identifier {{ param }}", - "delete": { - "question": "Are you sure you want to delete asset transaction {{ id }}?" - }, - "detail": { - "title": "Asset transaction" - }, - "documentDate": "Document date", - "valueDate": "Value date", - "action": "Action", - "amount": "Amount", - "remark": "Remark", - "membership": "Related membership" - } - } -} diff --git a/src/main/webapp/i18n/en/assetAction.json b/src/main/webapp/i18n/en/assetAction.json deleted file mode 100644 index e17447ca..00000000 --- a/src/main/webapp/i18n/en/assetAction.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "hsadminNgApp": { - "AssetAction": { - "null": "", - "PAYMENT": "Payment", - "HANDOVER": "Handover", - "ADOPTION": "Adoption", - "LOSS": "Loss", - "CLEARING": "Clearing", - "PAYBACK": "Payback" - } - } -} diff --git a/src/main/webapp/i18n/en/audits.json b/src/main/webapp/i18n/en/audits.json deleted file mode 100644 index ed5e16d4..00000000 --- a/src/main/webapp/i18n/en/audits.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "audits": { - "title": "Audits", - "filter": { - "title": "Filter per date", - "from": "from", - "to": "to", - "button": { - "weeks": "Weeks", - "today": "today", - "clear": "clear", - "close": "close" - } - }, - "table": { - "header": { - "principal": "User", - "date": "Date", - "status": "State", - "data": "Extra data" - }, - "data": { - "remoteAddress": "Remote Address:" - } - } - } -} diff --git a/src/main/webapp/i18n/en/configuration.json b/src/main/webapp/i18n/en/configuration.json deleted file mode 100644 index 81e208de..00000000 --- a/src/main/webapp/i18n/en/configuration.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "configuration": { - "title": "Configuration", - "filter": "Filter (by prefix)", - "table": { - "prefix": "Prefix", - "properties": "Properties" - } - } -} diff --git a/src/main/webapp/i18n/en/custom-error.json b/src/main/webapp/i18n/en/custom-error.json deleted file mode 100644 index 3ca13d6c..00000000 --- a/src/main/webapp/i18n/en/custom-error.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "error": { - "idNotFound": "Technical record-ID not found", - "unknownProperty": "Unknown Property", - "shareSubscriptionPositiveQuantity": "Share subscriptions require a positive quantity", - "shareCancellationNegativeQuantity": "Share cancellations require a negative quantity", - "shareTransactionImmutable": "Share transactions are immutable", - "membershipNotDeletable": "Membership cannot be deleted, instead set 'untilDate'", - "customerNotDeletable": "Customer cannot be deleted explicitly'", - "untilDateMustBeAfterSinceDate": "Membership until date must be after since date", - "anotherUncancelledMembershipExists": "Only a single uncancelled membership allowed per customer", - "initializationProhibited": "Initialization of the field prohibited", - "updateProhibited": "Update of the field prohibited", - "documentDateMayNotBeAfterValueDate": "Document date may not be after value date", - "assetTransactionImmutable": "Asset transactions are immutable", - "assetPaymentsPositiveAmount": "Asset payments require a positive amount", - "assetAdoptionsPositiveAmount": "Asset adoptions require a positive amount", - "assetPaybacksNegativeAmount": "Asset paybacks require a negative amount", - "assetHandoversNegativeAmount": "Asset handovers require a negative amount", - "assetLossesNegativeAmount": "Asset losses require a negative amount", - "assetClearingsNegativeAmount": "Asset clearings require a negative amount" - } -} diff --git a/src/main/webapp/i18n/en/customer.json b/src/main/webapp/i18n/en/customer.json deleted file mode 100644 index 6444cda2..00000000 --- a/src/main/webapp/i18n/en/customer.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "hsadminNgApp": { - "customer": { - "home": { - "title": "Customers", - "createLabel": "Create a new Customer", - "createOrEditLabel": "Create or edit a Customer" - }, - "created": "A new Customer is created with identifier {{ param }}", - "updated": "A Customer is updated with identifier {{ param }}", - "deleted": "A Customer is deleted with identifier {{ param }}", - "delete": { - "question": "Are you sure you want to delete Customer {{ id }}?" - }, - "detail": { - "title": "Customer" - }, - "reference": "Reference", - "prefix": "Prefix", - "name": "Name", - "kind": "Kind", - "birthDate": "Birth Date", - "birthPlace": "Birth Place", - "registrationCourt": "Registration Court", - "registrationNumber": "Registration Number", - "vatRegion": "Vat Region", - "vatNumber": "Vat Number", - "contractualSalutation": "Contractual Salutation", - "contractualAddress": "Contractual Address", - "billingSalutation": "Billing Salutation", - "billingAddress": "Billing Address", - "remark": "Remark", - "membership": "Membership", - "sepamandate": "Sepamandate" - } - } -} diff --git a/src/main/webapp/i18n/en/customerKind.json b/src/main/webapp/i18n/en/customerKind.json deleted file mode 100644 index f96e75e4..00000000 --- a/src/main/webapp/i18n/en/customerKind.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "hsadminNgApp": { - "CustomerKind": { - "null": "", - "NATURAL": "NATURAL", - "LEGAL": "LEGAL" - } - } -} diff --git a/src/main/webapp/i18n/en/error.json b/src/main/webapp/i18n/en/error.json deleted file mode 100644 index 4f8cc3e7..00000000 --- a/src/main/webapp/i18n/en/error.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "error": { - "title": "Error page!", - "http": { - "400": "Bad request.", - "403": "You are not authorized to access this page.", - "404": "The page does not exist.", - "405": "The HTTP verb you used is not supported for this URL.", - "500": "Internal server error." - }, - "concurrencyFailure": "Another user modified this data at the same time as you. Your changes were rejected.", - "validation": "Validation error on the server." - } -} diff --git a/src/main/webapp/i18n/en/global.json b/src/main/webapp/i18n/en/global.json deleted file mode 100644 index 72ca26ef..00000000 --- a/src/main/webapp/i18n/en/global.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "global": { - "title": "HsadminNg", - "browsehappy": "You are using an outdated browser. Please upgrade your browser to improve your experience.", - "menu": { - "home": "Home", - "jhipster-needle-menu-add-element": "JHipster will add additional menu entries here (do not translate!)", - "entities": { - "main": "Entities", - "customer": "Customer", - "membership": "Membership", - "share": "Share", - "asset": "Asset", - "sepaMandate": "Sepa Mandate", - "userRoleAssignment": "User Role Assignment", - "jhipster-needle-menu-add-entry": "JHipster will add additional entities here (do not translate!)" - }, - "account": { - "main": "Account", - "settings": "Settings", - "password": "Password", - "sessions": "Sessions", - "login": "Sign in", - "logout": "Sign out", - "register": "Register" - }, - "admin": { - "main": "Administration", - "userManagement": "User management", - "tracker": "User tracker", - "metrics": "Metrics", - "health": "Health", - "configuration": "Configuration", - "logs": "Logs", - "audits": "Audits", - "apidocs": "API", - "database": "Database", - "jhipster-needle-menu-add-admin-element": "JHipster will add additional menu entries here (do not translate!)" - }, - "language": "Language" - }, - "form": { - "username": "Username", - "username.placeholder": "Your username", - "currentpassword": "Current password", - "currentpassword.placeholder": "Current password", - "newpassword": "New password", - "newpassword.placeholder": "New password", - "confirmpassword": "New password confirmation", - "confirmpassword.placeholder": "Confirm the new password", - "email": "Email", - "email.placeholder": "Your email" - }, - "messages": { - "info": { - "authenticated": { - "prefix": "If you want to ", - "link": "sign in", - "suffix": ", you can try the default accounts:
- Administrator (login=\"admin\" and password=\"admin\")
- User (login=\"user\" and password=\"user\")." - }, - "register": { - "noaccount": "You don't have an account yet?", - "link": "Register a new account" - } - }, - "error": { - "dontmatch": "The password and its confirmation do not match!" - }, - "validate": { - "newpassword": { - "required": "Your password is required.", - "minlength": "Your password is required to be at least 4 characters.", - "maxlength": "Your password cannot be longer than 50 characters.", - "strength": "Password strength:" - }, - "confirmpassword": { - "required": "Your confirmation password is required.", - "minlength": "Your confirmation password is required to be at least 4 characters.", - "maxlength": "Your confirmation password cannot be longer than 50 characters." - }, - "email": { - "required": "Your email is required.", - "invalid": "Your email is invalid.", - "minlength": "Your email is required to be at least 5 characters.", - "maxlength": "Your email cannot be longer than 50 characters." - } - } - }, - "field": { - "id": "ID" - }, - "ribbon": { - "dev": "Development" - }, - "item-count": "Showing {{first}} - {{second}} of {{total}} items." - }, - "entity": { - "action": { - "addblob": "Add blob", - "addimage": "Add image", - "back": "Back", - "cancel": "Cancel", - "delete": "Delete", - "edit": "Edit", - "open": "Open", - "save": "Save", - "view": "View" - }, - "detail": { - "field": "Field", - "value": "Value" - }, - "delete": { - "title": "Confirm delete operation" - }, - "validation": { - "required": "This field is required.", - "minlength": "This field is required to be at least {{ min }} characters.", - "maxlength": "This field cannot be longer than {{ max }} characters.", - "min": "This field should be at least {{ min }}.", - "max": "This field cannot be more than {{ max }}.", - "minbytes": "This field should be at least {{ min }} bytes.", - "maxbytes": "This field cannot be more than {{ max }} bytes.", - "pattern": "This field should follow pattern for {{ pattern }}.", - "number": "This field should be a number.", - "datetimelocal": "This field should be a date and time.", - "patternLogin": "This field can only contain letters, digits and e-mail addresses.", - "duplicate": "This value is duplicate to existing data." - } - }, - "error": { - "internalServerError": "Internal server error", - "server.not.reachable": "Server not reachable", - "url.not.found": "Not found", - "NotNull": "Field {{ fieldName }} cannot be empty!", - "Size": "Field {{ fieldName }} does not meet min/max size requirements!", - "userexists": "Login name already used!", - "emailexists": "Email is already in use!", - "idexists": "A new {{ entityName }} cannot already have an ID", - "idnull": "Invalid ID" - }, - "footer": "This is your footer" -} diff --git a/src/main/webapp/i18n/en/health.json b/src/main/webapp/i18n/en/health.json deleted file mode 100644 index 9eba9c2d..00000000 --- a/src/main/webapp/i18n/en/health.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "health": { - "title": "Health Checks", - "refresh.button": "Refresh", - "stacktrace": "Stacktrace", - "details": { - "details": "Details", - "properties": "Properties", - "name": "Name", - "value": "Value", - "error": "Error" - }, - "indicator": { - "diskSpace": "Disk space", - "mail": "Email", - "db": "Database" - }, - "table": { - "service": "Service name", - "status": "Status" - }, - "status": { - "UNKNOWN": "UNKNOWN", - "UP": "UP", - "DOWN": "DOWN" - } - } -} diff --git a/src/main/webapp/i18n/en/home.json b/src/main/webapp/i18n/en/home.json deleted file mode 100644 index 402f1870..00000000 --- a/src/main/webapp/i18n/en/home.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "home": { - "title": "Welcome, Java Hipster!", - "subtitle": "This is your homepage", - "logged": { - "message": "You are logged in as user \"{{username}}\"." - }, - "question": "If you have any question on JHipster:", - "link": { - "homepage": "JHipster homepage", - "stackoverflow": "JHipster on Stack Overflow", - "bugtracker": "JHipster bug tracker", - "chat": "JHipster public chat room", - "follow": "follow @java_hipster on Twitter" - }, - "like": "If you like JHipster, don't forget to give us a star on", - "github": "GitHub" - } -} diff --git a/src/main/webapp/i18n/en/login.json b/src/main/webapp/i18n/en/login.json deleted file mode 100644 index 3552ddbf..00000000 --- a/src/main/webapp/i18n/en/login.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "login": { - "title": "Sign in", - "form": { - "password": "Password", - "password.placeholder": "Your password", - "rememberme": "Remember me", - "button": "Sign in" - }, - "messages": { - "error": { - "authentication": "Failed to sign in! Please check your credentials and try again." - } - }, - "password": { - "forgot": "Did you forget your password?" - } - } -} diff --git a/src/main/webapp/i18n/en/logs.json b/src/main/webapp/i18n/en/logs.json deleted file mode 100644 index a614b128..00000000 --- a/src/main/webapp/i18n/en/logs.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "logs": { - "title": "Logs", - "nbloggers": "There are {{ total }} loggers.", - "filter": "Filter", - "table": { - "name": "Name", - "level": "Level" - } - } -} diff --git a/src/main/webapp/i18n/en/membership.json b/src/main/webapp/i18n/en/membership.json deleted file mode 100644 index 30388671..00000000 --- a/src/main/webapp/i18n/en/membership.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "hsadminNgApp": { - "membership": { - "home": { - "title": "Memberships", - "createLabel": "Create a new Membership", - "createOrEditLabel": "Create or edit a Membership" - }, - "created": "A new Membership is created with identifier {{ param }}", - "updated": "A Membership is updated with identifier {{ param }}", - "deleted": "A Membership is deleted with identifier {{ param }}", - "delete": { - "question": "Are you sure you want to delete Membership {{ id }}?" - }, - "detail": { - "title": "Membership" - }, - "admissionDocumentDate": "Admission Document Date", - "cancellationDocumentDate": "Cancellation Document Date", - "memberFromDate": "Member From Date", - "memberUntilDate": "Member Until Date", - "remark": "Remark", - "share": "Share", - "asset": "Asset", - "customer": "Customer" - } - } -} diff --git a/src/main/webapp/i18n/en/metrics.json b/src/main/webapp/i18n/en/metrics.json deleted file mode 100644 index 7d379d20..00000000 --- a/src/main/webapp/i18n/en/metrics.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "metrics": { - "title": "Application Metrics", - "refresh.button": "Refresh", - "updating": "Updating...", - "jvm": { - "title": "JVM Metrics", - "memory": { - "title": "Memory", - "total": "Total Memory", - "heap": "Heap Memory", - "nonheap": "Non-Heap Memory" - }, - "threads": { - "title": "Threads", - "all": "All", - "runnable": "Runnable", - "timedwaiting": "Timed waiting", - "waiting": "Waiting", - "blocked": "Blocked", - "dump": { - "title": "Threads dump", - "id": "Id: ", - "blockedtime": "Blocked Time", - "blockedcount": "Blocked Count", - "waitedtime": "Waited Time", - "waitedcount": "Waited Count", - "lockname": "Lock name", - "stacktrace": "Stacktrace", - "show": "Show Stacktrace", - "hide": "Hide Stacktrace" - } - }, - "gc": { - "title": "Garbage collections", - "marksweepcount": "Mark Sweep count", - "marksweeptime": "Mark Sweep time", - "scavengecount": "Scavenge count", - "scavengetime": "Scavenge time" - }, - "http": { - "title": "HTTP requests (time in millisecond)", - "active": "Active requests:", - "total": "Total requests:", - "table": { - "code": "Code", - "count": "Count", - "mean": "Mean", - "average": "Average", - "max": "Max" - }, - "code": { - "ok": "Ok", - "notfound": "Not found", - "servererror": "Server Error" - } - } - }, - "servicesstats": { - "title": "Services statistics (time in millisecond)", - "table": { - "name": "Service name", - "count": "Count", - "mean": "Mean", - "min": "Min", - "max": "Max", - "p50": "p50", - "p75": "p75", - "p95": "p95", - "p99": "p99" - } - }, - "cache": { - "title": "Cache statistics", - "cachename": "Cache name", - "hits": "Cache Hits", - "misses": "Cache Misses", - "gets": "Cache Gets", - "puts": "Cache Puts", - "removals": "Cache Removals", - "evictions": "Cache Evictions", - "hitPercent": "Cache Hit %", - "missPercent": "Cache Miss %", - "averageGetTime": "Average get time (µs)", - "averagePutTime": "Average put time (µs)", - "averageRemoveTime": "Average remove time (µs)" - }, - "datasource": { - "usage": "Connection Pool Usage", - "title": "DataSource statistics (time in millisecond)", - "name": "Pool usage", - "count": "Count", - "mean": "Mean", - "min": "Min", - "max": "Max", - "p50": "p50", - "p75": "p75", - "p95": "p95", - "p99": "p99" - } - } -} diff --git a/src/main/webapp/i18n/en/password.json b/src/main/webapp/i18n/en/password.json deleted file mode 100644 index 7a7613f7..00000000 --- a/src/main/webapp/i18n/en/password.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "password": { - "title": "Password for [{{username}}]", - "form": { - "button": "Save" - }, - "messages": { - "error": "An error has occurred! The password could not be changed.", - "success": "Password changed!" - } - } -} diff --git a/src/main/webapp/i18n/en/register.json b/src/main/webapp/i18n/en/register.json deleted file mode 100644 index 3a03980b..00000000 --- a/src/main/webapp/i18n/en/register.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "register": { - "title": "Registration", - "form": { - "button": "Register" - }, - "messages": { - "validate": { - "login": { - "required": "Your username is required.", - "minlength": "Your username is required to be at least 1 character.", - "maxlength": "Your username cannot be longer than 50 characters.", - "pattern": "Your username can only contain letters and digits." - } - }, - "success": "Registration saved! Please check your email for confirmation.", - "error": { - "fail": "Registration failed! Please try again later.", - "userexists": "Login name already registered! Please choose another one.", - "emailexists": "Email is already in use! Please choose another one." - } - } - } -} diff --git a/src/main/webapp/i18n/en/reset.json b/src/main/webapp/i18n/en/reset.json deleted file mode 100644 index 6ceb949d..00000000 --- a/src/main/webapp/i18n/en/reset.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "reset": { - "request": { - "title": "Reset your password", - "form": { - "button": "Reset password" - }, - "messages": { - "info": "Enter the email address you used to register", - "success": "Check your emails for details on how to reset your password.", - "notfound": "Email address isn't registered! Please check and try again" - } - }, - "finish": { - "title": "Reset password", - "form": { - "button": "Validate new password" - }, - "messages": { - "info": "Choose a new password", - "success": "Your password has been reset. Please ", - "keymissing": "The reset key is missing.", - "error": "Your password couldn't be reset. Remember a password request is only valid for 24 hours." - } - } - } -} diff --git a/src/main/webapp/i18n/en/sepaMandate.json b/src/main/webapp/i18n/en/sepaMandate.json deleted file mode 100644 index d9aca2be..00000000 --- a/src/main/webapp/i18n/en/sepaMandate.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "hsadminNgApp": { - "sepaMandate": { - "home": { - "title": "Sepa Mandates", - "createLabel": "Create a new Sepa Mandate", - "createOrEditLabel": "Create or edit a Sepa Mandate" - }, - "created": "A new Sepa Mandate is created with identifier {{ param }}", - "updated": "A Sepa Mandate is updated with identifier {{ param }}", - "deleted": "A Sepa Mandate is deleted with identifier {{ param }}", - "delete": { - "question": "Are you sure you want to delete Sepa Mandate {{ id }}?" - }, - "detail": { - "title": "Sepa Mandate" - }, - "reference": "Reference", - "iban": "Iban", - "bic": "Bic", - "grantingDocumentDate": "Granting Document Date", - "revokationDocumentDate": "Revokation Document Date", - "validFromDate": "Valid From Date", - "validUntilDate": "Valid Until Date", - "lastUsedDate": "Last Used Date", - "remark": "Remark", - "customer": "Customer" - } - } -} diff --git a/src/main/webapp/i18n/en/sessions.json b/src/main/webapp/i18n/en/sessions.json deleted file mode 100644 index d410035e..00000000 --- a/src/main/webapp/i18n/en/sessions.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "sessions": { - "title": "Active sessions for [{{username}}]", - "table": { - "ipaddress": "IP address", - "useragent": "User Agent", - "date": "Date", - "button": "Invalidate" - }, - "messages": { - "success": "Session invalidated!", - "error": "An error has occurred! The session could not be invalidated." - } - } -} diff --git a/src/main/webapp/i18n/en/settings.json b/src/main/webapp/i18n/en/settings.json deleted file mode 100644 index d9413819..00000000 --- a/src/main/webapp/i18n/en/settings.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "settings": { - "title": "User settings for [{{username}}]", - "form": { - "firstname": "First Name", - "firstname.placeholder": "Your first name", - "lastname": "Last Name", - "lastname.placeholder": "Your last name", - "language": "Language", - "button": "Save" - }, - "messages": { - "error": { - "fail": "An error has occurred! Settings could not be saved.", - "emailexists": "Email is already in use! Please choose another one." - }, - "success": "Settings saved!", - "validate": { - "firstname": { - "required": "Your first name is required.", - "minlength": "Your first name is required to be at least 1 character", - "maxlength": "Your first name cannot be longer than 50 characters" - }, - "lastname": { - "required": "Your last name is required.", - "minlength": "Your last name is required to be at least 1 character", - "maxlength": "Your last name cannot be longer than 50 characters" - } - } - } - } -} diff --git a/src/main/webapp/i18n/en/share.json b/src/main/webapp/i18n/en/share.json deleted file mode 100644 index e103c0ec..00000000 --- a/src/main/webapp/i18n/en/share.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "hsadminNgApp": { - "share": { - "home": { - "title": "Share Transactions", - "createLabel": "Register a new share transaction", - "createOrEditLabel": "Register or edit a share transaction" - }, - "created": "A new share transactions is registered with identifier {{ param }}", - "updated": "A share transaction is updated with identifier {{ param }}", - "deleted": "A share transcation is deleted with identifier {{ param }}", - "delete": { - "question": "Are you sure you want to delete share transaction {{ id }}?" - }, - "detail": { - "title": "Share transaction" - }, - "documentDate": "Document date", - "valueDate": "Value date", - "action": "Action", - "quantity": "Quantity", - "remark": "Remark", - "membership": "Related membership" - } - } -} diff --git a/src/main/webapp/i18n/en/shareAction.json b/src/main/webapp/i18n/en/shareAction.json deleted file mode 100644 index b2aed341..00000000 --- a/src/main/webapp/i18n/en/shareAction.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "hsadminNgApp": { - "ShareAction": { - "null": "", - "SUBSCRIPTION": "Subscription", - "CANCELLATION": "Cancellation" - } - } -} diff --git a/src/main/webapp/i18n/en/user-management.json b/src/main/webapp/i18n/en/user-management.json deleted file mode 100644 index 30c125b6..00000000 --- a/src/main/webapp/i18n/en/user-management.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "userManagement": { - "home": { - "title": "Users", - "createLabel": "Create a new user", - "createOrEditLabel": "Create or edit a user" - }, - "created": "A new user is created with identifier {{ param }}", - "updated": "An user is updated with identifier {{ param }}", - "deleted": "An user is deleted with identifier {{ param }}", - "delete": { - "question": "Are you sure you want to delete user {{ login }}?" - }, - "detail": { - "title": "User" - }, - "login": "Login", - "firstName": "First name", - "lastName": "Last name", - "email": "Email", - "activated": "Activated", - "deactivated": "Deactivated", - "profiles": "Profiles", - "langKey": "Language", - "createdBy": "Created by", - "createdDate": "Created date", - "lastModifiedBy": "Modified by", - "lastModifiedDate": "Modified date" - } -} diff --git a/src/main/webapp/i18n/en/userRole.json b/src/main/webapp/i18n/en/userRole.json deleted file mode 100644 index 610dc2a4..00000000 --- a/src/main/webapp/i18n/en/userRole.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "hsadminNgApp": { - "UserRole": { - "null": "", - "HOSTMASTER": "HOSTMASTER", - "ADMIN": "ADMIN", - "SUPPORTER": "SUPPORTER", - "CONTRACTUAL_CONTACT": "CONTRACTUAL_CONTACT", - "FINANCIAL_CONTACT": "FINANCIAL_CONTACT", - "TECHNICAL_CONTACT": "TECHNICAL_CONTACT", - "CUSTOMER_USER": "CUSTOMER_USER" - } - } -} diff --git a/src/main/webapp/i18n/en/userRoleAssignment.json b/src/main/webapp/i18n/en/userRoleAssignment.json deleted file mode 100644 index 28225467..00000000 --- a/src/main/webapp/i18n/en/userRoleAssignment.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "hsadminNgApp": { - "userRoleAssignment": { - "home": { - "title": "User Role Assignments", - "createLabel": "Create a new User Role Assignment", - "createOrEditLabel": "Create or edit a User Role Assignment" - }, - "created": "A new User Role Assignment is created with identifier {{ param }}", - "updated": "A User Role Assignment is updated with identifier {{ param }}", - "deleted": "A User Role Assignment is deleted with identifier {{ param }}", - "delete": { - "question": "Are you sure you want to delete User Role Assignment {{ id }}?" - }, - "detail": { - "title": "User Role Assignment" - }, - "entityTypeId": "Entity Type Id", - "entityObjectId": "Entity Object Id", - "assignedRole": "Assigned Role", - "user": "User" - } - } -} diff --git a/src/main/webapp/i18n/en/vatRegion.json b/src/main/webapp/i18n/en/vatRegion.json deleted file mode 100644 index 339febc1..00000000 --- a/src/main/webapp/i18n/en/vatRegion.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "hsadminNgApp": { - "VatRegion": { - "null": "", - "DOMESTIC": "DOMESTIC", - "EU": "EU", - "OTHER": "OTHER" - } - } -} diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html deleted file mode 100644 index d912b1f0..00000000 --- a/src/main/webapp/index.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - hsadminNg - - - - - - - - - - - - -

-
-
-
-
-
-
-
-
- -
- - - - - - - diff --git a/src/main/webapp/manifest.webapp b/src/main/webapp/manifest.webapp deleted file mode 100644 index 4b6ddbb7..00000000 --- a/src/main/webapp/manifest.webapp +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "HsadminNg", - "short_name": "HsadminNg", - "icons": [ - { - "src": "./content/images/jhipster_family_member_3_head-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "./content/images/jhipster_family_member_3_head-256.png", - "sizes": "256x256", - "type": "image/png" - }, - { - "src": "./content/images/jhipster_family_member_3_head-384.png", - "sizes": "384x384", - "type": "image/png" - }, - { - "src": "./content/images/jhipster_family_member_3_head-512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#000000", - "background_color": "#e0e0e0", - "start_url": "/index.html", - "display": "standalone", - "orientation": "portrait" -} diff --git a/src/main/webapp/robots.txt b/src/main/webapp/robots.txt deleted file mode 100644 index 7cda2747..00000000 --- a/src/main/webapp/robots.txt +++ /dev/null @@ -1,11 +0,0 @@ -# robotstxt.org/ - -User-agent: * -Disallow: /api/account -Disallow: /api/account/change-password -Disallow: /api/account/sessions -Disallow: /api/audits/ -Disallow: /api/logs/ -Disallow: /api/users/ -Disallow: /management/ -Disallow: /v2/api-docs/ diff --git a/src/main/webapp/swagger-ui/dist/images/throbber.gif b/src/main/webapp/swagger-ui/dist/images/throbber.gif deleted file mode 100644 index 06393889242fb3ea9e0205fa84369ec7bb66d15a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9257 zcmd^^X;@R|x`tQg5wbE8AV3mAn1TjmQ&en2CK8~ENEH<+P_)pZ24y2E+7O0>K^a6u zQ3;5MiU^7p6*M3qDk!2=YEcHMQ>nzEYP;R`e2C@r+U+?#XaC*&gKPcB#k$`o&;7mu zYNhYYXe|Uo84#4ZIko#rcU5K8*yFL{qT47O&^5fZH$ zVZ@%(l~vVHjnm;H@KL8@r%yUHoo;rbHI_4lIH(_nsTT>S2`DFOD~uCb9_dF4`#QgI zy7ldMcLs+A_s%|e1pRPrbX-tpeNP!9(IpMFTce`t_5U%lP99z%&i6`1d~ zWeM!Rxc50<+d$e^9LT`?B+aMK~apR zHm?q;p<7{wN2g|I^aGlSws;VP84j(z%aQwvAWv83Z$}p(% zZ^?2;gxg(ey_`V5J7{;!o;o;KslW@z5EP~JGs|U)J7dF&(ff#A=6vU?cGQ$-4+;Jf z-ggJEa!yStn`_EWvl)#yhm6XVs}UUbsi;+agri;mCfjH^Uy;lH+Zw^h)4N?oZgZz4 zJk(fTZ|Bi^;+s_M=~+d#vyoxEPzTlOS=mX@sbl*uRj>=MaMr}cFIY8i?UM61>86uB zV$DlOUCiUJwbzJMP@D$urzK|lL2-PC!p1l47V-ZG<5Ev0Z5h~Kx?`KOp7gkAjV93A z-Gc7MrlxTf?wF;CbNc@tCHJH{TB3c;#{SVu%97}tyAM2n&|9W_?qv}$*Jt*%7Yxb# zV0;d;7|lDEltJYS+U)#aiJO};?_Jyy_4%syQ(uy?-J-Yx-9O5nKRk@@XSS~X<(2u~ zV-LamWm~!iqtH9wkpf8mAXZhOD&L#aA_%)4h2M;1M5jt zIR>Us+%W-GXa_f^opKg=DSrAs)AXeRa;Hp0aC1OgbxQ%Qr_QvTleM1jkR!2mkcX$3 ztsR8~G9iqh(-FJ@F_rQBIYDXV_6s7G9SxaVF^laZqcx$!D97m|7t16j6@Jt6UdDRy49Qyvs|c>RuA|@b%}`*wU}2^7q;&Vtc6@lb zcXl)T!6nYDzmMJ~%n$KNXyNlCG)GkJ4!82;v6@d3>s5r~E+3!O?049JDr14Y^PeMI02R`0lJ^=oJ zYd|*u9|SU(j7hY?+<=(?fP*mtV*zFhOrz6%{VA?ozdm&(Jf^V zMfPZ?>l`mS3{Uq8IM;e!+1YjJy2!mzK$O|wPeU{*QSbs9m+@`f5KxO3PBnQ=%RsZg%go*fJ`*w9TL{-WgZVIA$!YV}3BRcfeXaR$x#b zW)Tpd#8E4)^MyYdkH;4_;ChJuw%n+Be7Ko4;w-nHvyo$d_0e-YiF78Df&)_)(}fcr_r0mPH(4RRYWIu+d@t0&Ss@O^s! zOKyX&13)%N@83r^;QsgN{rl(!0|RF1FA)b1{CRXAy&1ySz@>olPiR4r$aMdq&_=nK zq|cFs8phWJ1@%dZ-gXd{zDbTILD>)qEvH-NU*Rf1b2J1Ri79`rBFl@ z8E^0I)OqEi{pH(a24b9YPG;Kz@t-qZW;3Mpe`MRlmYx{7bH-XZ&`RQ7Rb^%}gc&X| zd}Q-FZf|RWxHU?PR!(C?80zu(^l>*h{#ulSiid(O!J(8P-41bNM3tnX@U6NS5yo0? zdcF)~xFE&+&|gZ$23dV5t~?$$&ymZ;F8j7GGMncGSsDo%>J`26=&l=X#rSKv_64;0 zr;k6no@=gV`P)K!=kaHl>q?!`X>(A;84tg^Md<`zA%qbRLby1Z=fn*ZRdNqs%Tq|3 zOt}lZu0q9oKJhgz&+^7PCt$=UFW=R*w?a1)ePoL*`R$Gxj?TU@12tTHsT$giHQU+sqf;fS0FpT!< z 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 - - - - Swagger UI - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
-
- - diff --git a/src/test/features/user/user.feature b/src/test/features/user/user.feature deleted file mode 100644 index cffd53f0..00000000 --- a/src/test/features/user/user.feature +++ /dev/null @@ -1,6 +0,0 @@ -Feature: User management - - Scenario: Retrieve administrator user - When I search user 'admin' - Then the user is not found - And his last name is 'Administrator' diff --git a/src/test/java/org/hostsharing/hsadminng/config/WebConfigurerTest.java b/src/test/java/org/hostsharing/hsadminng/config/WebConfigurerTest.java deleted file mode 100644 index bd2b6447..00000000 --- a/src/test/java/org/hostsharing/hsadminng/config/WebConfigurerTest.java +++ /dev/null @@ -1,193 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import io.github.jhipster.config.JHipsterConstants; -import io.github.jhipster.config.JHipsterProperties; -import io.github.jhipster.web.filter.CachingHttpHeadersFilter; -import io.undertow.Undertow; -import io.undertow.Undertow.Builder; -import io.undertow.UndertowOptions; - -import org.apache.commons.io.FilenameUtils; -import org.h2.server.web.WebServlet; -import org.junit.Before; -import org.junit.Test; -import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.mock.env.MockEnvironment; -import org.springframework.mock.web.MockServletContext; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.xnio.OptionMap; - -import java.util.*; - -import javax.servlet.*; - -/** - * Unit tests for the WebConfigurer class. - * - * @see WebConfigurer - */ -public class WebConfigurerTest { - - private WebConfigurer webConfigurer; - - private MockServletContext servletContext; - - private MockEnvironment env; - - private JHipsterProperties props; - - @Before - public void setup() { - servletContext = spy(new MockServletContext()); - doReturn(mock(FilterRegistration.Dynamic.class)) - .when(servletContext) - .addFilter(anyString(), any(Filter.class)); - doReturn(mock(ServletRegistration.Dynamic.class)) - .when(servletContext) - .addServlet(anyString(), any(Servlet.class)); - - env = new MockEnvironment(); - props = new JHipsterProperties(); - - webConfigurer = new WebConfigurer(env, props); - } - - @Test - public void testStartUpProdServletContext() throws ServletException { - env.setActiveProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION); - webConfigurer.onStartup(servletContext); - - verify(servletContext).addFilter(eq("cachingHttpHeadersFilter"), any(CachingHttpHeadersFilter.class)); - verify(servletContext, never()).addServlet(eq("H2Console"), any(WebServlet.class)); - } - - @Test - public void testStartUpDevServletContext() throws ServletException { - env.setActiveProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT); - webConfigurer.onStartup(servletContext); - - verify(servletContext, never()).addFilter(eq("cachingHttpHeadersFilter"), any(CachingHttpHeadersFilter.class)); - verify(servletContext).addServlet(eq("H2Console"), any(WebServlet.class)); - } - - @Test - public void testCustomizeServletContainer() { - env.setActiveProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION); - UndertowServletWebServerFactory container = new UndertowServletWebServerFactory(); - webConfigurer.customize(container); - assertThat(container.getMimeMappings().get("abs")).isEqualTo("audio/x-mpeg"); - assertThat(container.getMimeMappings().get("html")).isEqualTo("text/html;charset=utf-8"); - assertThat(container.getMimeMappings().get("json")).isEqualTo("text/html;charset=utf-8"); - if (container.getDocumentRoot() != null) { - assertThat(container.getDocumentRoot().getPath()).isEqualTo(FilenameUtils.separatorsToSystem("build/www")); - } - - Builder builder = Undertow.builder(); - container.getBuilderCustomizers().forEach(c -> c.customize(builder)); - OptionMap.Builder serverOptions = (OptionMap.Builder) ReflectionTestUtils.getField(builder, "serverOptions"); - assertThat(serverOptions.getMap().get(UndertowOptions.ENABLE_HTTP2)).isNull(); - } - - @Test - public void testUndertowHttp2Enabled() { - props.getHttp().setVersion(JHipsterProperties.Http.Version.V_2_0); - UndertowServletWebServerFactory container = new UndertowServletWebServerFactory(); - webConfigurer.customize(container); - Builder builder = Undertow.builder(); - container.getBuilderCustomizers().forEach(c -> c.customize(builder)); - OptionMap.Builder serverOptions = (OptionMap.Builder) ReflectionTestUtils.getField(builder, "serverOptions"); - assertThat(serverOptions.getMap().get(UndertowOptions.ENABLE_HTTP2)).isTrue(); - } - - @Test - public void testCorsFilterOnApiPath() throws Exception { - props.getCors().setAllowedOrigins(Collections.singletonList("*")); - props.getCors().setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); - props.getCors().setAllowedHeaders(Collections.singletonList("*")); - props.getCors().setMaxAge(1800L); - props.getCors().setAllowCredentials(true); - - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()) - .addFilters(webConfigurer.corsFilter()) - .build(); - - mockMvc.perform( - options("/api/test-cors") - .header(HttpHeaders.ORIGIN, "other.domain.com") - .header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST")) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "other.domain.com")) - .andExpect(header().string(HttpHeaders.VARY, "Origin")) - .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET,POST,PUT,DELETE")) - .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true")) - .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "1800")); - - mockMvc.perform( - get("/api/test-cors") - .header(HttpHeaders.ORIGIN, "other.domain.com")) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "other.domain.com")); - } - - @Test - public void testCorsFilterOnOtherPath() throws Exception { - props.getCors().setAllowedOrigins(Collections.singletonList("*")); - props.getCors().setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); - props.getCors().setAllowedHeaders(Collections.singletonList("*")); - props.getCors().setMaxAge(1800L); - props.getCors().setAllowCredentials(true); - - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()) - .addFilters(webConfigurer.corsFilter()) - .build(); - - mockMvc.perform( - get("/test/test-cors") - .header(HttpHeaders.ORIGIN, "other.domain.com")) - .andExpect(status().isOk()) - .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - } - - @Test - public void testCorsFilterDeactivated() throws Exception { - props.getCors().setAllowedOrigins(null); - - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()) - .addFilters(webConfigurer.corsFilter()) - .build(); - - mockMvc.perform( - get("/api/test-cors") - .header(HttpHeaders.ORIGIN, "other.domain.com")) - .andExpect(status().isOk()) - .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - } - - @Test - public void testCorsFilterDeactivated2() throws Exception { - props.getCors().setAllowedOrigins(new ArrayList<>()); - - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()) - .addFilters(webConfigurer.corsFilter()) - .build(); - - mockMvc.perform( - get("/api/test-cors") - .header(HttpHeaders.ORIGIN, "other.domain.com")) - .andExpect(status().isOk()) - .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/config/WebConfigurerTestController.java b/src/test/java/org/hostsharing/hsadminng/config/WebConfigurerTestController.java deleted file mode 100644 index c866cd51..00000000 --- a/src/test/java/org/hostsharing/hsadminng/config/WebConfigurerTestController.java +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class WebConfigurerTestController { - - @GetMapping("/api/test-cors") - public void testCorsOnApiPath() { - } - - @GetMapping("/test/test-cors") - public void testCorsOnOtherPath() { - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/config/timezone/HibernateTimeZoneTest.java b/src/test/java/org/hostsharing/hsadminng/config/timezone/HibernateTimeZoneTest.java deleted file mode 100644 index bc81adb9..00000000 --- a/src/test/java/org/hostsharing/hsadminng/config/timezone/HibernateTimeZoneTest.java +++ /dev/null @@ -1,178 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.config.timezone; - -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.repository.timezone.DateTimeWrapper; -import org.hostsharing.hsadminng.repository.timezone.DateTimeWrapperRepository; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.rowset.SqlRowSet; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.transaction.annotation.Transactional; - -import java.time.*; -import java.time.format.DateTimeFormatter; - -/** - * Unit tests for the UTC Hibernate configuration. - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class HibernateTimeZoneTest { - - @Autowired - private DateTimeWrapperRepository dateTimeWrapperRepository; - @Autowired - private JdbcTemplate jdbcTemplate; - - private DateTimeWrapper dateTimeWrapper; - private DateTimeFormatter dateTimeFormatter; - private DateTimeFormatter timeFormatter; - private DateTimeFormatter dateFormatter; - - @Before - public void setup() { - dateTimeWrapper = new DateTimeWrapper(); - dateTimeWrapper.setInstant(Instant.parse("2014-11-12T05:50:00.0Z")); - dateTimeWrapper.setLocalDateTime(LocalDateTime.parse("2014-11-12T07:50:00.0")); - dateTimeWrapper.setOffsetDateTime(OffsetDateTime.parse("2011-12-14T08:30:00.0Z")); - dateTimeWrapper.setZonedDateTime(ZonedDateTime.parse("2011-12-14T08:30:00.0Z")); - dateTimeWrapper.setLocalTime(LocalTime.parse("14:30:00")); - dateTimeWrapper.setOffsetTime(OffsetTime.parse("14:30:00+02:00")); - dateTimeWrapper.setLocalDate(LocalDate.parse("2016-09-10")); - - dateTimeFormatter = DateTimeFormatter - .ofPattern("yyyy-MM-dd HH:mm:ss.S") - .withZone(ZoneId.of("UTC")); - - timeFormatter = DateTimeFormatter - .ofPattern("HH:mm:ss") - .withZone(ZoneId.of("UTC")); - - dateFormatter = DateTimeFormatter - .ofPattern("yyyy-MM-dd"); - } - - @Test - @Transactional - public void storeInstantWithUtcConfigShouldBeStoredOnGMTTimeZone() { - dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); - - String request = generateSqlRequest("instant", dateTimeWrapper.getId()); - SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); - String expectedValue = dateTimeFormatter.format(dateTimeWrapper.getInstant()); - - assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); - } - - @Test - @Transactional - public void storeLocalDateTimeWithUtcConfigShouldBeStoredOnGMTTimeZone() { - dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); - - String request = generateSqlRequest("local_date_time", dateTimeWrapper.getId()); - SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); - String expectedValue = dateTimeWrapper - .getLocalDateTime() - .atZone(ZoneId.systemDefault()) - .format(dateTimeFormatter); - - assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); - } - - @Test - @Transactional - public void storeOffsetDateTimeWithUtcConfigShouldBeStoredOnGMTTimeZone() { - dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); - - String request = generateSqlRequest("offset_date_time", dateTimeWrapper.getId()); - SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); - String expectedValue = dateTimeWrapper - .getOffsetDateTime() - .format(dateTimeFormatter); - - assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); - } - - @Test - @Transactional - public void storeZoneDateTimeWithUtcConfigShouldBeStoredOnGMTTimeZone() { - dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); - - String request = generateSqlRequest("zoned_date_time", dateTimeWrapper.getId()); - SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); - String expectedValue = dateTimeWrapper - .getZonedDateTime() - .format(dateTimeFormatter); - - assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); - } - - @Test - @Transactional - public void storeLocalTimeWithUtcConfigShouldBeStoredOnGMTTimeZoneAccordingToHis1stJan1970Value() { - dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); - - String request = generateSqlRequest("local_time", dateTimeWrapper.getId()); - SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); - String expectedValue = dateTimeWrapper - .getLocalTime() - .atDate(LocalDate.of(1970, Month.JANUARY, 1)) - .atZone(ZoneId.systemDefault()) - .format(timeFormatter); - - assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); - } - - @Test - @Transactional - public void storeOffsetTimeWithUtcConfigShouldBeStoredOnGMTTimeZoneAccordingToHis1stJan1970Value() { - dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); - - String request = generateSqlRequest("offset_time", dateTimeWrapper.getId()); - SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); - String expectedValue = dateTimeWrapper - .getOffsetTime() - .toLocalTime() - .atDate(LocalDate.of(1970, Month.JANUARY, 1)) - .atZone(ZoneId.systemDefault()) - .format(timeFormatter); - - assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); - } - - @Test - @Transactional - public void storeLocalDateWithUtcConfigShouldBeStoredWithoutTransformation() { - dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); - - String request = generateSqlRequest("local_date", dateTimeWrapper.getId()); - SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); - String expectedValue = dateTimeWrapper - .getLocalDate() - .format(dateFormatter); - - assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); - } - - private String generateSqlRequest(String fieldName, long id) { - return format("SELECT %s FROM jhi_date_time_wrapper where id=%d", fieldName, id); - } - - private void assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(SqlRowSet sqlRowSet, String expectedValue) { - while (sqlRowSet.next()) { - String dbValue = sqlRowSet.getString(1); - - assertThat(dbValue).isNotNull(); - assertThat(dbValue).isEqualTo(expectedValue); - } - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/cucumber/CucumberContextConfiguration.java b/src/test/java/org/hostsharing/hsadminng/cucumber/CucumberContextConfiguration.java deleted file mode 100644 index 09ebc4bc..00000000 --- a/src/test/java/org/hostsharing/hsadminng/cucumber/CucumberContextConfiguration.java +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.cucumber; - -import org.hostsharing.hsadminng.HsadminNgApp; - -import cucumber.api.java.Before; - -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.web.WebAppConfiguration; - -@SpringBootTest -@WebAppConfiguration -@ContextConfiguration(classes = HsadminNgApp.class) -public class CucumberContextConfiguration { - - @Before - public void setup_cucumber_spring_context() { - // Dummy method so cucumber will recognize this class as glue - // and use its context configuration. - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/cucumber/CucumberTest.java b/src/test/java/org/hostsharing/hsadminng/cucumber/CucumberTest.java deleted file mode 100644 index 5d292157..00000000 --- a/src/test/java/org/hostsharing/hsadminng/cucumber/CucumberTest.java +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.cucumber; - -import cucumber.api.CucumberOptions; -import cucumber.api.junit.Cucumber; - -import org.junit.runner.RunWith; - -@RunWith(Cucumber.class) -@CucumberOptions(plugin = "pretty", features = "src/test/features") -public class CucumberTest { - -} diff --git a/src/test/java/org/hostsharing/hsadminng/cucumber/stepdefs/StepDefs.java b/src/test/java/org/hostsharing/hsadminng/cucumber/stepdefs/StepDefs.java deleted file mode 100644 index 1d0ea7a4..00000000 --- a/src/test/java/org/hostsharing/hsadminng/cucumber/stepdefs/StepDefs.java +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.cucumber.stepdefs; - -import org.springframework.test.web.servlet.ResultActions; - -public abstract class StepDefs { - - protected ResultActions actions; - -} diff --git a/src/test/java/org/hostsharing/hsadminng/cucumber/stepdefs/UserStepDefs.java b/src/test/java/org/hostsharing/hsadminng/cucumber/stepdefs/UserStepDefs.java deleted file mode 100644 index fc5f77de..00000000 --- a/src/test/java/org/hostsharing/hsadminng/cucumber/stepdefs/UserStepDefs.java +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.cucumber.stepdefs; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.web.rest.UserResource; - -import cucumber.api.java.Before; -import cucumber.api.java.en.Then; -import cucumber.api.java.en.When; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -public class UserStepDefs extends StepDefs { - - @Autowired - private UserResource userResource; - - private MockMvc restUserMockMvc; - - @Before - public void setup() { - this.restUserMockMvc = MockMvcBuilders.standaloneSetup(userResource).build(); - } - - @When("I search user {string}") - public void i_search_user(String userId) throws Throwable { - actions = restUserMockMvc.perform( - get("/api/users/" + userId) - .accept(MediaType.APPLICATION_JSON)); - } - - @Then("the user is found") - public void the_user_is_found() throws Throwable { - actions - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); - } - - @Then("his last name is {string}") - public void his_last_name_is(String lastName) throws Throwable { - actions.andExpect(jsonPath("$.lastName").value(lastName)); - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/liquibase/ReplaceCustomChangeUnitTest.java b/src/test/java/org/hostsharing/hsadminng/liquibase/ReplaceCustomChangeUnitTest.java deleted file mode 100644 index 53c971c8..00000000 --- a/src/test/java/org/hostsharing/hsadminng/liquibase/ReplaceCustomChangeUnitTest.java +++ /dev/null @@ -1,165 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.liquibase; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.ThrowableAssert.catchThrowable; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verify; - -import liquibase.database.Database; -import liquibase.database.jvm.JdbcConnection; -import liquibase.exception.CustomChangeException; -import liquibase.exception.DatabaseException; -import liquibase.exception.SetupException; - -import com.google.common.base.VerifyException; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.sql.SQLException; -import java.sql.Statement; - -public class ReplaceCustomChangeUnitTest { - - private static final String POSTGRES_DATABASE_PRODUCT_NAME = "PostgreSQL"; - private static final String H2_DATABASE_PRODUCT_NAME = "H2"; - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private Database database; - - @Mock - private JdbcConnection connection; - - @Mock - private Statement statement; - - @Before - public void initMocks() throws DatabaseException { - given(database.getConnection()).willReturn(connection); - given(connection.getAutoCommit()).willReturn(false); - given(connection.createStatement()).willReturn(statement); - } - - @Test - public void updatesForPostgres() throws Exception { - // given - given(database.getDatabaseProductName()).willReturn(POSTGRES_DATABASE_PRODUCT_NAME); - final ReplaceCustomChange replaceCustomChange = givenReplaceCustomChange("some_table", "address,remark", "|", "\\n"); - - // when - replaceCustomChange.execute(database); - - // then - verify(statement).executeUpdate("UPDATE some_table SET address= replace(address, '|', E'\\n')"); - verify(statement).executeUpdate("UPDATE some_table SET remark= replace(remark, '|', E'\\n')"); - } - - @Test - public void updatesForH2() throws Exception { - // given - given(database.getDatabaseProductName()).willReturn(H2_DATABASE_PRODUCT_NAME); - final ReplaceCustomChange replaceCustomChange = givenReplaceCustomChange("some_table", "address,remark", "|", "\\n"); - - // when - replaceCustomChange.execute(database); - - // then - verify(statement).executeUpdate("UPDATE some_table SET address= replace(address, '|', STRINGDECODE('\\n'))"); - verify(statement).executeUpdate("UPDATE some_table SET remark= replace(remark, '|', STRINGDECODE('\\n'))"); - } - - @Test - public void getConfirmationMessage() throws Exception { - // given - final ReplaceCustomChange replaceCustomChange = givenReplaceCustomChange("some_table", "address,remark", "|", "\\n"); - - // when - final String actual = replaceCustomChange.getConfirmationMessage(); - - // then - assertThat(actual).isEqualTo("in table some_table / columns address,remark: replaced all '|' to '\\n'"); - } - - @Test - public void throwsValidationErrorIfAutoCommitIsOff() throws Exception { - // given - given(database.getDatabaseProductName()).willReturn(POSTGRES_DATABASE_PRODUCT_NAME); - final ReplaceCustomChange replaceCustomChange = givenReplaceCustomChange("some_table", "address,remark", "|", "\\n"); - given(connection.getAutoCommit()).willReturn(true); - - // when - final Throwable actual = catchThrowable(() -> replaceCustomChange.execute(database)); - - // then - assertThat(actual).isInstanceOf(VerifyException.class); - } - - @Test - public void onDatabaseExceptionThrowsCustomChangeException() throws Exception { - // given - given(database.getDatabaseProductName()).willReturn(POSTGRES_DATABASE_PRODUCT_NAME); - final ReplaceCustomChange replaceCustomChange = givenReplaceCustomChange("some_table", "address,remark", "|", "\\n"); - final Exception givenCausingException = new DatabaseException("dummy"); - given(connection.createStatement()).willThrow(givenCausingException); - - // when - final Throwable actual = catchThrowable(() -> replaceCustomChange.execute(database)); - - // then - assertThat(actual).isInstanceOfSatisfying( - CustomChangeException.class, - (cce) -> assertThat(cce.getCause()).isSameAs(givenCausingException)); - } - - @Test - public void onSQLExceptionThrowsCustomChangeException() throws Exception { - // given - given(database.getDatabaseProductName()).willReturn(POSTGRES_DATABASE_PRODUCT_NAME); - final ReplaceCustomChange replaceCustomChange = givenReplaceCustomChange("some_table", "address,remark", "|", "\\n"); - final Exception givenCausingException = new SQLException("dummy"); - given(statement.executeUpdate(anyString())).willThrow(givenCausingException); - - // when - final Throwable actual = catchThrowable(() -> replaceCustomChange.execute(database)); - - // then - assertThat(actual).isInstanceOfSatisfying( - CustomChangeException.class, - (cce) -> assertThat(cce.getCause()).isSameAs(givenCausingException)); - } - - @Test - public void setFileOpenerDoesNothing() { - new ReplaceCustomChange().setFileOpener(null); - } - - @Test - public void validateDoesNothing() { - new ReplaceCustomChange().validate(null); - } - - // --- only test fixture below --- - - private ReplaceCustomChange givenReplaceCustomChange( - final String some_table, - final String columns, - final String searchFor, - final String replaceWith) throws SetupException { - final ReplaceCustomChange replaceCustomChange = new ReplaceCustomChange(); - replaceCustomChange.setUp(); - replaceCustomChange.setTableName(some_table); - replaceCustomChange.setColumnNames(columns); - replaceCustomChange.setSearchFor(searchFor); - replaceCustomChange.setReplaceWith(replaceWith); - return replaceCustomChange; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/repository/AssetRepositoryIntTest.java b/src/test/java/org/hostsharing/hsadminng/repository/AssetRepositoryIntTest.java deleted file mode 100644 index 2bceb8b6..00000000 --- a/src/test/java/org/hostsharing/hsadminng/repository/AssetRepositoryIntTest.java +++ /dev/null @@ -1,76 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.transaction.annotation.Transactional; - -import java.math.BigDecimal; -import java.time.LocalDate; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -@Transactional -public class AssetRepositoryIntTest { - - @Autowired - private CustomerRepository customerRepository; - - @Autowired - private MembershipRepository membershipRepository; - - @Autowired - private AssetRepository assetRepository; - - @Test - public void sequenceStartsAbove1000000ToSpareIdsForSampleData() { - // given - final Asset givenAsset = createArbitraryAsset(); - - // when - assetRepository.save(givenAsset); - - // then - assertThat(givenAsset.getId()).isGreaterThan(1000000); - } - - // --- only test fixture below --- - - private Customer createPersistentCustomer() { - return customerRepository.save(CustomerRepositoryIntTest.createCustomer()); - } - - private Membership createPersistentMembership() { - return membershipRepository - .save(MembershipRepositoryIntTest.createMembership(createPersistentCustomer(), "2019-01-08", null)); - } - - static Asset createAsset( - final Membership membership, - final AssetAction action, - final String amount, - final String documentDate) { - final Asset asset = new Asset(); - asset.setMembership(membership); - asset.setAction(action); - asset.setAmount(new BigDecimal(amount)); - asset.setDocumentDate(LocalDate.parse(documentDate)); - asset.setValueDate(LocalDate.parse(documentDate).plusDays(1)); - return asset; - } - - private Asset createArbitraryAsset() { - return createAsset(createPersistentMembership(), AssetAction.PAYMENT, "160.00", "2019-01-08"); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/repository/CustomAuditEventRepositoryIntTest.java b/src/test/java/org/hostsharing/hsadminng/repository/CustomAuditEventRepositoryIntTest.java deleted file mode 100644 index a66d3a6d..00000000 --- a/src/test/java/org/hostsharing/hsadminng/repository/CustomAuditEventRepositoryIntTest.java +++ /dev/null @@ -1,168 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hostsharing.hsadminng.repository.CustomAuditEventRepository.EVENT_DATA_COLUMN_MAX_LENGTH; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.config.Constants; -import org.hostsharing.hsadminng.config.audit.AuditEventConverter; -import org.hostsharing.hsadminng.domain.PersistentAuditEvent; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.audit.AuditEvent; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpSession; -import org.springframework.security.web.authentication.WebAuthenticationDetails; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpSession; - -/** - * Test class for the CustomAuditEventRepository class. - * - * @see CustomAuditEventRepository - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -@Transactional -public class CustomAuditEventRepositoryIntTest { - - @Autowired - private PersistenceAuditEventRepository persistenceAuditEventRepository; - - @Autowired - private AuditEventConverter auditEventConverter; - - private CustomAuditEventRepository customAuditEventRepository; - - private PersistentAuditEvent testUserEvent; - - private PersistentAuditEvent testOtherUserEvent; - - private PersistentAuditEvent testOldUserEvent; - - @Before - public void setup() { - customAuditEventRepository = new CustomAuditEventRepository(persistenceAuditEventRepository, auditEventConverter); - persistenceAuditEventRepository.deleteAll(); - Instant oneHourAgo = Instant.now().minusSeconds(3600); - - testUserEvent = new PersistentAuditEvent(); - testUserEvent.setPrincipal("test-user"); - testUserEvent.setAuditEventType("test-type"); - testUserEvent.setAuditEventDate(oneHourAgo); - Map data = new HashMap<>(); - data.put("test-key", "test-value"); - testUserEvent.setData(data); - - testOldUserEvent = new PersistentAuditEvent(); - testOldUserEvent.setPrincipal("test-user"); - testOldUserEvent.setAuditEventType("test-type"); - testOldUserEvent.setAuditEventDate(oneHourAgo.minusSeconds(10000)); - - testOtherUserEvent = new PersistentAuditEvent(); - testOtherUserEvent.setPrincipal("other-test-user"); - testOtherUserEvent.setAuditEventType("test-type"); - testOtherUserEvent.setAuditEventDate(oneHourAgo); - } - - @Test - public void addAuditEvent() { - Map data = new HashMap<>(); - data.put("test-key", "test-value"); - AuditEvent event = new AuditEvent("test-user", "test-type", data); - customAuditEventRepository.add(event); - List persistentAuditEvents = persistenceAuditEventRepository.findAll(); - assertThat(persistentAuditEvents).hasSize(1); - PersistentAuditEvent persistentAuditEvent = persistentAuditEvents.get(0); - assertThat(persistentAuditEvent.getPrincipal()).isEqualTo(event.getPrincipal()); - assertThat(persistentAuditEvent.getAuditEventType()).isEqualTo(event.getType()); - assertThat(persistentAuditEvent.getData()).containsKey("test-key"); - assertThat(persistentAuditEvent.getData().get("test-key")).isEqualTo("test-value"); - assertThat(persistentAuditEvent.getAuditEventDate()).isEqualTo(event.getTimestamp()); - } - - @Test - public void addAuditEventTruncateLargeData() { - Map data = new HashMap<>(); - StringBuilder largeData = new StringBuilder(); - for (int i = 0; i < EVENT_DATA_COLUMN_MAX_LENGTH + 10; i++) { - largeData.append("a"); - } - data.put("test-key", largeData); - AuditEvent event = new AuditEvent("test-user", "test-type", data); - customAuditEventRepository.add(event); - List persistentAuditEvents = persistenceAuditEventRepository.findAll(); - assertThat(persistentAuditEvents).hasSize(1); - PersistentAuditEvent persistentAuditEvent = persistentAuditEvents.get(0); - assertThat(persistentAuditEvent.getPrincipal()).isEqualTo(event.getPrincipal()); - assertThat(persistentAuditEvent.getAuditEventType()).isEqualTo(event.getType()); - assertThat(persistentAuditEvent.getData()).containsKey("test-key"); - String actualData = persistentAuditEvent.getData().get("test-key"); - assertThat(actualData.length()).isEqualTo(EVENT_DATA_COLUMN_MAX_LENGTH); - assertThat(actualData).isSubstringOf(largeData); - assertThat(persistentAuditEvent.getAuditEventDate()).isEqualTo(event.getTimestamp()); - } - - @Test - public void testAddEventWithWebAuthenticationDetails() { - HttpSession session = new MockHttpSession(null, "test-session-id"); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setSession(session); - request.setRemoteAddr("1.2.3.4"); - WebAuthenticationDetails details = new WebAuthenticationDetails(request); - Map data = new HashMap<>(); - data.put("test-key", details); - AuditEvent event = new AuditEvent("test-user", "test-type", data); - customAuditEventRepository.add(event); - List persistentAuditEvents = persistenceAuditEventRepository.findAll(); - assertThat(persistentAuditEvents).hasSize(1); - PersistentAuditEvent persistentAuditEvent = persistentAuditEvents.get(0); - assertThat(persistentAuditEvent.getData().get("remoteAddress")).isEqualTo("1.2.3.4"); - assertThat(persistentAuditEvent.getData().get("sessionId")).isEqualTo("test-session-id"); - } - - @Test - public void testAddEventWithNullData() { - Map data = new HashMap<>(); - data.put("test-key", null); - AuditEvent event = new AuditEvent("test-user", "test-type", data); - customAuditEventRepository.add(event); - List persistentAuditEvents = persistenceAuditEventRepository.findAll(); - assertThat(persistentAuditEvents).hasSize(1); - PersistentAuditEvent persistentAuditEvent = persistentAuditEvents.get(0); - assertThat(persistentAuditEvent.getData().get("test-key")).isEqualTo("null"); - } - - @Test - public void addAuditEventWithAnonymousUser() { - Map data = new HashMap<>(); - data.put("test-key", "test-value"); - AuditEvent event = new AuditEvent(Constants.ANONYMOUS_USER, "test-type", data); - customAuditEventRepository.add(event); - List persistentAuditEvents = persistenceAuditEventRepository.findAll(); - assertThat(persistentAuditEvents).hasSize(0); - } - - @Test - public void addAuditEventWithAuthorizationFailureType() { - Map data = new HashMap<>(); - data.put("test-key", "test-value"); - AuditEvent event = new AuditEvent("test-user", "AUTHORIZATION_FAILURE", data); - customAuditEventRepository.add(event); - List persistentAuditEvents = persistenceAuditEventRepository.findAll(); - assertThat(persistentAuditEvents).hasSize(0); - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/repository/CustomerRepositoryIntTest.java b/src/test/java/org/hostsharing/hsadminng/repository/CustomerRepositoryIntTest.java deleted file mode 100644 index 3b517140..00000000 --- a/src/test/java/org/hostsharing/hsadminng/repository/CustomerRepositoryIntTest.java +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.enumeration.CustomerKind; -import org.hostsharing.hsadminng.domain.enumeration.VatRegion; - -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.RandomUtils; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.transaction.annotation.Transactional; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -@Transactional -public class CustomerRepositoryIntTest { - - @Autowired - private CustomerRepository customerRepository; - - @Test - public void sequenceStartsAbove1000000ToSpareIdsForSampleData() { - // given - final Customer givenCustomer = createCustomer(); - - // when - customerRepository.save(givenCustomer); - - // then - assertThat(givenCustomer.getId()).isGreaterThan(1000000); - } - - // --- only test fixture below --- - - static Customer createCustomer() { - final Customer customer = new Customer(); - customer.setPrefix(RandomStringUtils.randomAlphabetic(3).toLowerCase()); - customer.setReference(RandomUtils.nextInt(10001, 19999)); - customer.setName(RandomStringUtils.randomAlphabetic(10)); - customer.setContractualAddress(RandomStringUtils.randomAlphabetic(10)); - customer.setKind(CustomerKind.NATURAL); - customer.setVatRegion(VatRegion.DOMESTIC); - return customer; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/repository/MembershipRepositoryIntTest.java b/src/test/java/org/hostsharing/hsadminng/repository/MembershipRepositoryIntTest.java deleted file mode 100644 index 3b38815d..00000000 --- a/src/test/java/org/hostsharing/hsadminng/repository/MembershipRepositoryIntTest.java +++ /dev/null @@ -1,104 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Membership; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDate; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -@Transactional -public class MembershipRepositoryIntTest { - - @Autowired - private CustomerRepository customerRepository; - - @Autowired - private MembershipRepository membershipRepository; - - @Test - public void sequenceStartsAbove1000000ToSpareIdsForSampleData() { - // given - final Membership givenMembership = createMembership(createPersistentCustomer(), "2019-01-01", null); - - // when - membershipRepository.save(givenMembership); - - // then - assertThat(givenMembership.getId()).isGreaterThan(1000000); - } - - @Test - public void hasUncancelledMembershipForCustomerIsTrueForCustomerWithUncancelledMembership() { - // given - final Customer givenCustomerWithUncancelledMembership = createPersistentCustomerWithMembership("2011-08-18", null); - - // when - boolean actual = membershipRepository - .hasUncancelledMembershipForCustomer(givenCustomerWithUncancelledMembership.getId()); - - // then - assertThat(actual).isTrue(); - } - - @Test - public void hasUncancelledMembershipForCustomerIsFalseForCustomerWithoutMembership() { - // given - final Customer givenCustomerWithoutMembership = createPersistentCustomer(); - - // when - boolean actual = membershipRepository.hasUncancelledMembershipForCustomer(givenCustomerWithoutMembership.getId()); - - // then - assertThat(actual).isFalse(); - } - - @Test - public void hasUncancelledMembershipForCustomerIsFalseForCustomerWithCancelledMembership() { - // given - final Customer givenCustomerWithCancelledMembership = createPersistentCustomerWithMembership( - "2011-08-18", - "2017-12-31"); - - // when - boolean actual = membershipRepository.hasUncancelledMembershipForCustomer(givenCustomerWithCancelledMembership.getId()); - - // then - assertThat(actual).isFalse(); - } - - // --- only test fixture below --- - - private Customer createPersistentCustomer() { - return customerRepository.save(CustomerRepositoryIntTest.createCustomer()); - } - - private Customer createPersistentCustomerWithMembership(final String from, final String to) { - final Customer customer = createPersistentCustomer(); - final Membership membership = createMembership(customer, from, to); - membershipRepository.save(membership); - return customer; - } - - static Membership createMembership(final Customer customer, final String from, final String to) { - final Membership membership = new Membership(); - membership.setCustomer(customer); - membership.setMemberFromDate(LocalDate.parse(from)); - if (to != null) { - membership.setMemberUntilDate(LocalDate.parse(to)); - } - membership.setAdmissionDocumentDate(membership.getMemberFromDate().minusDays(7)); - return membership; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/repository/SepaMandateRepositoryIntTest.java b/src/test/java/org/hostsharing/hsadminng/repository/SepaMandateRepositoryIntTest.java deleted file mode 100644 index 6e23fe75..00000000 --- a/src/test/java/org/hostsharing/hsadminng/repository/SepaMandateRepositoryIntTest.java +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.SepaMandate; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDate; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -@Transactional -public class SepaMandateRepositoryIntTest { - - @Autowired - private CustomerRepository customerRepository; - - @Autowired - private SepaMandateRepository sepaMandateRepository; - - @Test - public void sequenceStartsAbove1000000ToSpareIdsForSampleData() { - // given - final SepaMandate givenSepaMandate = createSepaMandate(createPersistentCustomer(), "DUMMY_REF", "2019-01-08", null); - - // when - sepaMandateRepository.save(givenSepaMandate); - - // then - assertThat(givenSepaMandate.getId()).isGreaterThan(1000000); - } - - // --- only test fixture below --- - - private Customer createPersistentCustomer() { - return customerRepository.save(CustomerRepositoryIntTest.createCustomer()); - } - - static SepaMandate createSepaMandate(final Customer customer, final String reference, final String from, final String to) { - final SepaMandate sepaMandate = new SepaMandate(); - sepaMandate.setCustomer(customer); - sepaMandate.setReference(reference); - sepaMandate.setIban("NL57ABNA2228161411"); - sepaMandate.setBic("ABNANL2A"); - sepaMandate.setGrantingDocumentDate(LocalDate.parse(from)); - sepaMandate.setValidFromDate(LocalDate.parse(from).plusDays(1)); - if (to != null) { - sepaMandate.setRevokationDocumentDate(LocalDate.parse(to)); - sepaMandate.setValidUntilDate(LocalDate.parse(to).plusDays(7)); - } - return sepaMandate; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/repository/ShareRepositoryIntTest.java b/src/test/java/org/hostsharing/hsadminng/repository/ShareRepositoryIntTest.java deleted file mode 100644 index 25489811..00000000 --- a/src/test/java/org/hostsharing/hsadminng/repository/ShareRepositoryIntTest.java +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDate; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -@Transactional -public class ShareRepositoryIntTest { - - @Autowired - private CustomerRepository customerRepository; - - @Autowired - private MembershipRepository membershipRepository; - - @Autowired - private ShareRepository shareRepository; - - @Test - public void sequenceStartsAbove1000000ToSpareIdsForSampleData() { - // given - final Share givenShare = createArbitraryShare(); - - // when - shareRepository.save(givenShare); - - // then - assertThat(givenShare.getId()).isGreaterThan(1000000); - } - - // --- only test fixture below --- - - private Customer createPersistentCustomer() { - return customerRepository.save(CustomerRepositoryIntTest.createCustomer()); - } - - private Membership createPersistentMembership() { - return membershipRepository - .save(MembershipRepositoryIntTest.createMembership(createPersistentCustomer(), "2019-01-08", null)); - } - - static Share createShare( - final Membership membership, - final ShareAction action, - final int quantity, - final String documentDate) { - final Share share = new Share(); - share.setMembership(membership); - share.setAction(action); - share.setQuantity(quantity); - share.setDocumentDate(LocalDate.parse(documentDate)); - share.setValueDate(LocalDate.parse(documentDate).plusDays(1)); - return share; - } - - private Share createArbitraryShare() { - return createShare(createPersistentMembership(), ShareAction.SUBSCRIPTION, 1, "2019-01-08"); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/repository/timezone/DateTimeWrapper.java b/src/test/java/org/hostsharing/hsadminng/repository/timezone/DateTimeWrapper.java deleted file mode 100644 index c3d86649..00000000 --- a/src/test/java/org/hostsharing/hsadminng/repository/timezone/DateTimeWrapper.java +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository.timezone; - -import java.io.Serializable; -import java.time.*; -import java.util.Objects; - -import javax.persistence.*; - -@Entity -@Table(name = "jhi_date_time_wrapper") -public class DateTimeWrapper implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") - @SequenceGenerator(name = "sequenceGenerator") - private Long id; - - @Column(name = "instant") - private Instant instant; - - @Column(name = "local_date_time") - private LocalDateTime localDateTime; - - @Column(name = "offset_date_time") - private OffsetDateTime offsetDateTime; - - @Column(name = "zoned_date_time") - private ZonedDateTime zonedDateTime; - - @Column(name = "local_time") - private LocalTime localTime; - - @Column(name = "offset_time") - private OffsetTime offsetTime; - - @Column(name = "local_date") - private LocalDate localDate; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Instant getInstant() { - return instant; - } - - public void setInstant(Instant instant) { - this.instant = instant; - } - - public LocalDateTime getLocalDateTime() { - return localDateTime; - } - - public void setLocalDateTime(LocalDateTime localDateTime) { - this.localDateTime = localDateTime; - } - - public OffsetDateTime getOffsetDateTime() { - return offsetDateTime; - } - - public void setOffsetDateTime(OffsetDateTime offsetDateTime) { - this.offsetDateTime = offsetDateTime; - } - - public ZonedDateTime getZonedDateTime() { - return zonedDateTime; - } - - public void setZonedDateTime(ZonedDateTime zonedDateTime) { - this.zonedDateTime = zonedDateTime; - } - - public LocalTime getLocalTime() { - return localTime; - } - - public void setLocalTime(LocalTime localTime) { - this.localTime = localTime; - } - - public OffsetTime getOffsetTime() { - return offsetTime; - } - - public void setOffsetTime(OffsetTime offsetTime) { - this.offsetTime = offsetTime; - } - - public LocalDate getLocalDate() { - return localDate; - } - - public void setLocalDate(LocalDate localDate) { - this.localDate = localDate; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - DateTimeWrapper dateTimeWrapper = (DateTimeWrapper) o; - return !(dateTimeWrapper.getId() == null || getId() == null) && Objects.equals(getId(), dateTimeWrapper.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(getId()); - } - - @Override - public String toString() { - return "TimeZoneTest{" + - "id=" + id + - ", instant=" + instant + - ", localDateTime=" + localDateTime + - ", offsetDateTime=" + offsetDateTime + - ", zonedDateTime=" + zonedDateTime + - '}'; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/repository/timezone/DateTimeWrapperRepository.java b/src/test/java/org/hostsharing/hsadminng/repository/timezone/DateTimeWrapperRepository.java deleted file mode 100644 index 1eea9b1d..00000000 --- a/src/test/java/org/hostsharing/hsadminng/repository/timezone/DateTimeWrapperRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.repository.timezone; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -/** - * Spring Data JPA repository for the DateTimeWrapper entity. - */ -@Repository -public interface DateTimeWrapperRepository extends JpaRepository { - -} diff --git a/src/test/java/org/hostsharing/hsadminng/security/DomainUserDetailsServiceIntTest.java b/src/test/java/org/hostsharing/hsadminng/security/DomainUserDetailsServiceIntTest.java deleted file mode 100644 index 78bfa455..00000000 --- a/src/test/java/org/hostsharing/hsadminng/security/DomainUserDetailsServiceIntTest.java +++ /dev/null @@ -1,128 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.repository.UserRepository; - -import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Locale; - -/** - * Test class for DomainUserDetailsService. - * - * @see DomainUserDetailsService - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -@Transactional -public class DomainUserDetailsServiceIntTest { - - private static final String USER_ONE_LOGIN = "test-user-one"; - private static final String USER_ONE_EMAIL = "test-user-one@localhost"; - private static final String USER_TWO_LOGIN = "test-user-two"; - private static final String USER_TWO_EMAIL = "test-user-two@localhost"; - private static final String USER_THREE_LOGIN = "test-user-three"; - private static final String USER_THREE_EMAIL = "test-user-three@localhost"; - - @Autowired - private UserRepository userRepository; - - @Autowired - private UserDetailsService domainUserDetailsService; - - private User userOne; - private User userTwo; - private User userThree; - - @Before - public void init() { - userOne = new User(); - userOne.setLogin(USER_ONE_LOGIN); - userOne.setPassword(RandomStringUtils.random(60)); - userOne.setActivated(true); - userOne.setEmail(USER_ONE_EMAIL); - userOne.setFirstName("userOne"); - userOne.setLastName("doe"); - userOne.setLangKey("en"); - userRepository.save(userOne); - - userTwo = new User(); - userTwo.setLogin(USER_TWO_LOGIN); - userTwo.setPassword(RandomStringUtils.random(60)); - userTwo.setActivated(true); - userTwo.setEmail(USER_TWO_EMAIL); - userTwo.setFirstName("userTwo"); - userTwo.setLastName("doe"); - userTwo.setLangKey("en"); - userRepository.save(userTwo); - - userThree = new User(); - userThree.setLogin(USER_THREE_LOGIN); - userThree.setPassword(RandomStringUtils.random(60)); - userThree.setActivated(false); - userThree.setEmail(USER_THREE_EMAIL); - userThree.setFirstName("userThree"); - userThree.setLastName("doe"); - userThree.setLangKey("en"); - userRepository.save(userThree); - } - - @Test - @Transactional - public void assertThatUserCanBeFoundByLogin() { - UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_ONE_LOGIN); - assertThat(userDetails).isNotNull(); - assertThat(userDetails.getUsername()).isEqualTo(USER_ONE_LOGIN); - } - - @Test - @Transactional - public void assertThatUserCanBeFoundByLoginIgnoreCase() { - UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_ONE_LOGIN.toUpperCase(Locale.ENGLISH)); - assertThat(userDetails).isNotNull(); - assertThat(userDetails.getUsername()).isEqualTo(USER_ONE_LOGIN); - } - - @Test - @Transactional - public void assertThatUserCanBeFoundByEmail() { - UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_TWO_EMAIL); - assertThat(userDetails).isNotNull(); - assertThat(userDetails.getUsername()).isEqualTo(USER_TWO_LOGIN); - } - - @Test(expected = UsernameNotFoundException.class) - @Transactional - public void assertThatUserCanNotBeFoundByEmailIgnoreCase() { - domainUserDetailsService.loadUserByUsername(USER_TWO_EMAIL.toUpperCase(Locale.ENGLISH)); - } - - @Test - @Transactional - public void assertThatEmailIsPrioritizedOverLogin() { - UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_ONE_EMAIL); - assertThat(userDetails).isNotNull(); - assertThat(userDetails.getUsername()).isEqualTo(USER_ONE_LOGIN); - } - - @Test(expected = UserNotActivatedException.class) - @Transactional - public void assertThatUserNotActivatedExceptionIsThrownForNotActivatedUsers() { - domainUserDetailsService.loadUserByUsername(USER_THREE_LOGIN); - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/security/SecurityUtilsUnitTest.java b/src/test/java/org/hostsharing/hsadminng/security/SecurityUtilsUnitTest.java deleted file mode 100644 index 05769287..00000000 --- a/src/test/java/org/hostsharing/hsadminng/security/SecurityUtilsUnitTest.java +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Optional; - -/** - * Test class for the SecurityUtils utility class. - * - * @see SecurityUtils - */ -public class SecurityUtilsUnitTest { - - @Test - public void testgetCurrentUserLogin() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); - SecurityContextHolder.setContext(securityContext); - Optional login = SecurityUtils.getCurrentUserLogin(); - assertThat(login).contains("admin"); - } - - @Test - public void testgetCurrentUserJWT() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "token")); - SecurityContextHolder.setContext(securityContext); - Optional jwt = SecurityUtils.getCurrentUserJWT(); - assertThat(jwt).contains("token"); - } - - @Test - public void testIsAuthenticated() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); - SecurityContextHolder.setContext(securityContext); - boolean isAuthenticated = SecurityUtils.isAuthenticated(); - assertThat(isAuthenticated).isTrue(); - } - - @Test - public void testAnonymousIsNotAuthenticated() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - Collection authorities = new ArrayList<>(); - authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); - securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities)); - SecurityContextHolder.setContext(securityContext); - boolean isAuthenticated = SecurityUtils.isAuthenticated(); - assertThat(isAuthenticated).isFalse(); - } - - @Test - public void testIsCurrentUserInRole() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - Collection authorities = new ArrayList<>(); - authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.USER)); - securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("user", "user", authorities)); - SecurityContextHolder.setContext(securityContext); - - assertThat(SecurityUtils.isCurrentUserInRole(AuthoritiesConstants.USER)).isTrue(); - assertThat(SecurityUtils.isCurrentUserInRole(AuthoritiesConstants.ADMIN)).isFalse(); - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/security/jwt/JWTFilterTest.java b/src/test/java/org/hostsharing/hsadminng/security/jwt/JWTFilterTest.java deleted file mode 100644 index ac77496a..00000000 --- a/src/test/java/org/hostsharing/hsadminng/security/jwt/JWTFilterTest.java +++ /dev/null @@ -1,119 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security.jwt; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.security.AuthoritiesConstants; - -import io.github.jhipster.config.JHipsterProperties; -import io.jsonwebtoken.io.Decoders; -import io.jsonwebtoken.security.Keys; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.http.HttpStatus; -import org.springframework.mock.web.MockFilterChain; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.Collections; - -public class JWTFilterTest { - - private TokenProvider tokenProvider; - - private JWTFilter jwtFilter; - - @Before - public void setup() { - JHipsterProperties jHipsterProperties = new JHipsterProperties(); - tokenProvider = new TokenProvider(jHipsterProperties); - ReflectionTestUtils.setField( - tokenProvider, - "key", - Keys.hmacShaKeyFor( - Decoders.BASE64 - .decode( - "fd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8"))); - - ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", 60000); - jwtFilter = new JWTFilter(tokenProvider); - SecurityContextHolder.getContext().setAuthentication(null); - } - - @Test - public void testJWTFilter() throws Exception { - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( - "test-user", - "test-password", - Collections.singletonList(new SimpleGrantedAuthority(AuthoritiesConstants.USER))); - String jwt = tokenProvider.createToken(authentication, false); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); - request.setRequestURI("/api/test"); - MockHttpServletResponse response = new MockHttpServletResponse(); - MockFilterChain filterChain = new MockFilterChain(); - jwtFilter.doFilter(request, response, filterChain); - assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); - assertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo("test-user"); - assertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials().toString()).isEqualTo(jwt); - } - - @Test - public void testJWTFilterInvalidToken() throws Exception { - String jwt = "wrong_jwt"; - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); - request.setRequestURI("/api/test"); - MockHttpServletResponse response = new MockHttpServletResponse(); - MockFilterChain filterChain = new MockFilterChain(); - jwtFilter.doFilter(request, response, filterChain); - assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); - } - - @Test - public void testJWTFilterMissingAuthorization() throws Exception { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setRequestURI("/api/test"); - MockHttpServletResponse response = new MockHttpServletResponse(); - MockFilterChain filterChain = new MockFilterChain(); - jwtFilter.doFilter(request, response, filterChain); - assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); - } - - @Test - public void testJWTFilterMissingToken() throws Exception { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader(JWTFilter.AUTHORIZATION_HEADER, "Bearer "); - request.setRequestURI("/api/test"); - MockHttpServletResponse response = new MockHttpServletResponse(); - MockFilterChain filterChain = new MockFilterChain(); - jwtFilter.doFilter(request, response, filterChain); - assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); - } - - @Test - public void testJWTFilterWrongScheme() throws Exception { - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( - "test-user", - "test-password", - Collections.singletonList(new SimpleGrantedAuthority(AuthoritiesConstants.USER))); - String jwt = tokenProvider.createToken(authentication, false); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader(JWTFilter.AUTHORIZATION_HEADER, "Basic " + jwt); - request.setRequestURI("/api/test"); - MockHttpServletResponse response = new MockHttpServletResponse(); - MockFilterChain filterChain = new MockFilterChain(); - jwtFilter.doFilter(request, response, filterChain); - assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/security/jwt/TokenProviderTest.java b/src/test/java/org/hostsharing/hsadminng/security/jwt/TokenProviderTest.java deleted file mode 100644 index d1e6934a..00000000 --- a/src/test/java/org/hostsharing/hsadminng/security/jwt/TokenProviderTest.java +++ /dev/null @@ -1,116 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.security.jwt; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.security.AuthoritiesConstants; - -import io.github.jhipster.config.JHipsterProperties; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.io.Decoders; -import io.jsonwebtoken.security.Keys; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.test.util.ReflectionTestUtils; - -import java.security.Key; -import java.util.*; - -public class TokenProviderTest { - - private final long ONE_MINUTE = 60000; - private Key key; - private JHipsterProperties jHipsterProperties; - private TokenProvider tokenProvider; - - @Before - public void setup() { - jHipsterProperties = Mockito.mock(JHipsterProperties.class); - tokenProvider = new TokenProvider(jHipsterProperties); - key = Keys.hmacShaKeyFor( - Decoders.BASE64 - .decode( - "fd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8")); - - ReflectionTestUtils.setField(tokenProvider, "key", key); - ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", ONE_MINUTE); - } - - @Test - public void testReturnFalseWhenJWThasInvalidSignature() { - boolean isTokenValid = tokenProvider.validateToken(createTokenWithDifferentSignature()); - - assertThat(isTokenValid).isEqualTo(false); - } - - @Test - public void testReturnFalseWhenJWTisMalformed() { - Authentication authentication = createAuthentication(); - String token = tokenProvider.createToken(authentication, false); - String invalidToken = token.substring(1); - boolean isTokenValid = tokenProvider.validateToken(invalidToken); - - assertThat(isTokenValid).isEqualTo(false); - } - - @Test - public void testReturnFalseWhenJWTisExpired() { - ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", -ONE_MINUTE); - - Authentication authentication = createAuthentication(); - String token = tokenProvider.createToken(authentication, false); - - boolean isTokenValid = tokenProvider.validateToken(token); - - assertThat(isTokenValid).isEqualTo(false); - } - - @Test - public void testReturnFalseWhenJWTisUnsupported() { - String unsupportedToken = createUnsupportedToken(); - - boolean isTokenValid = tokenProvider.validateToken(unsupportedToken); - - assertThat(isTokenValid).isEqualTo(false); - } - - @Test - public void testReturnFalseWhenJWTisInvalid() { - boolean isTokenValid = tokenProvider.validateToken(""); - - assertThat(isTokenValid).isEqualTo(false); - } - - private Authentication createAuthentication() { - Collection authorities = new ArrayList<>(); - authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); - return new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities); - } - - private String createUnsupportedToken() { - return Jwts.builder() - .setPayload("payload") - .signWith(key, SignatureAlgorithm.HS512) - .compact(); - } - - private String createTokenWithDifferentSignature() { - Key otherKey = Keys.hmacShaKeyFor( - Decoders.BASE64 - .decode( - "Xfd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8")); - - return Jwts.builder() - .setSubject("anonymous") - .signWith(otherKey, SignatureAlgorithm.HS512) - .setExpiration(new Date(new Date().getTime() + ONE_MINUTE)) - .compact(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/AssetServiceUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/AssetServiceUnitTest.java deleted file mode 100644 index dd7eb4e2..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/AssetServiceUnitTest.java +++ /dev/null @@ -1,180 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowableOfType; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; - -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; -import org.hostsharing.hsadminng.repository.AssetRepository; -import org.hostsharing.hsadminng.service.dto.AssetDTO; -import org.hostsharing.hsadminng.service.mapper.AssetMapper; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.apache.commons.lang3.RandomUtils; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.math.BigDecimal; - -import javax.persistence.EntityManager; - -// HINT: In IntelliJ IDEA such unit test classes can be created with Shift-Ctrl-T. -// Do not forget to amend the class name (.e.g. ...UnitTest / ...IntTest)! -public class AssetServiceUnitTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private EntityManager em; - - @Mock - private AssetRepository assetRepository; - - @Mock - private AssetValidator assetValidator; // needed for @InjectMocks assetService - - @Mock - private AssetMapper assetMapper; - - @InjectMocks - private AssetService assetService; - - // HINT: Click outside of any test method (e.g. here) and use Ctrl-Shift-F10 - // to run all tests from this test class. Use Ctrl-F5 to run the last execution again; - // 'execution' here can also apply to running the application, whatever ran last. - - // HINT: In IntelliJ IDEA such test methods can be created with Alt-Insert. - @Test - public void deleteIsRejectedForAssetTransactions() { - // when - final Throwable throwException = catchThrowableOfType( - () -> assetService.delete(RandomUtils.nextLong()), - BadRequestAlertException.class); - - // then - // HINT: When using auto-import for assertions (e.g. via Alt-Enter in IntelliJ IDEA), - // beware to use the correct candidate from org.assertj.core.api.Assertions. - assertThat(throwException).isEqualToComparingFieldByField( - new BadRequestAlertException("Asset transactions are immutable", "asset", "assetTransactionImmutable")); - } - - @Test - public void saveShouldPersistValidTransactions() { - // given - final AssetDTO givenAssetDTO = givenAssetDTO(null, AssetAction.PAYMENT, anyPositiveAmout()); - // HINT: given(...)...will...() can't be used for void methods, in that case use Mockito's do...() methods - doNothing().when(assetValidator).validate(givenAssetDTO); - - // when - final AssetDTO returnedAssetDto = assetService.save(givenAssetDTO); - - // then - verify(em).flush(); - verify(em).refresh(any(Asset.class)); - assertThat(returnedAssetDto).isEqualToIgnoringGivenFields(givenAssetDTO, "id"); - } - - @Test - public void saveShouldNotPersistInvalidTransactions() { - // given - final AssetDTO givenAssetDTO = givenAssetDTO(null, AssetAction.PAYMENT, anyNegativeAmount()); - doThrow(new BadRequestAlertException("Some Dummy Test Violation", "asset", "assetInvalidTestDummy")) - .when(assetValidator) - .validate(givenAssetDTO); - - // when - final Throwable throwException = catchThrowableOfType( - () -> assetService.save(givenAssetDTO), - BadRequestAlertException.class); - - // then - assertThat(throwException).isEqualToComparingFieldByField( - new BadRequestAlertException("Some Dummy Test Violation", "asset", "assetInvalidTestDummy")); - } - - @Test - public void saveShouldUpdateValidTransactions() { - // given - final AssetDTO givenAssetDTO = givenAssetDTO(anyNonNullId(), AssetAction.PAYMENT, anyPositiveAmout()); - doNothing().when(assetValidator).validate(givenAssetDTO); - - // when - final AssetDTO returnedAssetDto = assetService.save(givenAssetDTO); - - // then - verify(em).flush(); - verify(em).refresh(any(Asset.class)); - assertThat(returnedAssetDto).isEqualToIgnoringGivenFields(givenAssetDTO, "id"); - } - - @Test - public void saveShouldNotUpdateInvalidTransactions() { - // given - final AssetDTO givenAssetDTO = givenAssetDTO(anyNonNullId(), AssetAction.PAYMENT, anyNegativeAmount()); - // HINT: given(...) can't be used for void methods, in that case use Mockito's do...() methods - doThrow(new BadRequestAlertException("Some Dummy Test Violation", "asset", "assetInvalidTestDummy")) - .when(assetValidator) - .validate(givenAssetDTO); - - // when - final Throwable throwException = catchThrowableOfType( - () -> assetService.save(givenAssetDTO), - BadRequestAlertException.class); - - // then - assertThat(throwException).isEqualToComparingFieldByField( - new BadRequestAlertException("Some Dummy Test Violation", "asset", "assetInvalidTestDummy")); - } - - // --- only test fixture code below --- - - private long anyNonNullId() { - return RandomUtils.nextInt(); - } - - // HINT: This rather complicated setup indicates that the method AssetService::save breaks the single responsibility - // principle. - private AssetDTO givenAssetDTO(final Long id, final AssetAction givenAction, final BigDecimal givenQuantity) { - final AssetDTO givenAssetDTO = createAssetDTO(id, givenAction, givenQuantity); - - // dto -> entity - final Asset givenAssetEntity = Mockito.mock(Asset.class); - given(assetMapper.toEntity(same(givenAssetDTO))).willReturn(givenAssetEntity); - - // assetRepository.save(entity); - final Asset persistedAssetEntity = Mockito.mock(Asset.class); - given(assetRepository.save(same(givenAssetEntity))).willReturn(persistedAssetEntity); - - // entity -> dto - AssetDTO persistedAssetDTO = createAssetDTO(id == null ? RandomUtils.nextLong() : id, givenAction, givenQuantity); - given(assetMapper.toDto(same(persistedAssetEntity))).willReturn(persistedAssetDTO); - - return givenAssetDTO; - } - - private AssetDTO createAssetDTO(Long id, AssetAction givenAction, BigDecimal givenAmount) { - final AssetDTO givenAssetDTO = new AssetDTO(); - givenAssetDTO.setId(id); - givenAssetDTO.setAction(givenAction); - givenAssetDTO.setAmount(givenAmount); - return givenAssetDTO; - } - - private BigDecimal anyPositiveAmout() { - return BigDecimal.valueOf(RandomUtils.nextInt()).add(new BigDecimal("0.1")); - } - - private BigDecimal anyNegativeAmount() { - return anyPositiveAmout().negate(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/AssetValidatorUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/AssetValidatorUnitTest.java deleted file mode 100644 index 69442072..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/AssetValidatorUnitTest.java +++ /dev/null @@ -1,318 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowableOfType; - -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; -import org.hostsharing.hsadminng.service.dto.AssetDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import com.google.common.collect.ImmutableList; - -import org.apache.commons.lang3.RandomUtils; -import org.assertj.core.api.AbstractThrowableAssert; -import org.junit.Test; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.function.Consumer; - -public class AssetValidatorUnitTest { - - private AssetValidator assetValidator = new AssetValidator(); - - @Test - public void shouldAcceptValidIncreasingTransaction() { - for (AssetAction action : ImmutableList.of(AssetAction.PAYMENT, AssetAction.ADOPTION)) { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(action) - .withAmount("64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isNull(); - } - } - - @Test - public void shouldAcceptValidDecreasingTransaction() { - for (AssetAction action : ImmutableList - .of(AssetAction.PAYBACK, AssetAction.HANDOVER, AssetAction.CLEARING, AssetAction.LOSS)) { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(action) - .withAmount("-64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isNull(); - } - } - - @Test - public void shouldAcceptIfDocumentDateEqualsValueDate() { - new GivenAssetValidationTestCase() - .withDocumentDate("2019-04-11") - .withValueDate("2019-04-11") - .withAction(AssetAction.PAYMENT) - .withAmount("64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isNull(); - } - - @Test - public void shouldRejectUpdates() { - new GivenAssetValidationTestCase() - .withId(RandomUtils.nextLong()) - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset transactions are immutable", - "asset", - "assetTransactionImmutable")); - } - - @Test - public void shouldRejectIfDocumentDateAfterValueDate() { - new GivenAssetValidationTestCase() - .withDocumentDate("2019-04-13") - .withValueDate("2019-04-12") - .withAction(AssetAction.PAYMENT) - .withAmount("64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Document date may not be after value date", - "asset", - "documentDateMayNotBeAfterValueDate")); - } - - @Test - public void shouldRejectIfPaymentWithNegativeAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.PAYMENT) - .withAmount("-64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset payments require a positive amount", - "asset", - "assetPaymentsPositiveAmount")); - } - - @Test - public void shouldRejectIfPaymentWithZeroAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.PAYMENT) - .withAmount("0.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset payments require a positive amount", - "asset", - "assetPaymentsPositiveAmount")); - } - - @Test - public void shouldRejectIfAdoptionWithNegativeAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.ADOPTION) - .withAmount("-64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset adoptions require a positive amount", - "asset", - "assetAdoptionsPositiveAmount")); - } - - @Test - public void shouldRejectIfAdoptionWithZeroAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.ADOPTION) - .withAmount("0.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset adoptions require a positive amount", - "asset", - "assetAdoptionsPositiveAmount")); - } - - @Test - public void shouldRejectIfPaybackWithPositiveAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.PAYBACK) - .withAmount("64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset paybacks require a negative amount", - "asset", - "assetPaybacksNegativeAmount")); - } - - @Test - public void shouldRejectIfPaybackWithZeroAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.PAYBACK) - .withAmount("0.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset paybacks require a negative amount", - "asset", - "assetPaybacksNegativeAmount")); - } - - @Test - public void shouldRejectIfHandoverWithPositiveAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.HANDOVER) - .withAmount("64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset handovers require a negative amount", - "asset", - "assetHandoversNegativeAmount")); - } - - @Test - public void shouldRejectIfHandoverWithZeroAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.HANDOVER) - .withAmount("0.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset handovers require a negative amount", - "asset", - "assetHandoversNegativeAmount")); - } - - @Test - public void shouldRejectIfLossWithPositiveAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.LOSS) - .withAmount("64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset losses require a negative amount", - "asset", - "assetLossesNegativeAmount")); - } - - @Test - public void shouldRejectIfLossWithZeroAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.LOSS) - .withAmount("0.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset losses require a negative amount", - "asset", - "assetLossesNegativeAmount")); - } - - @Test - public void shouldRejectIfClearingWithPositiveAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.CLEARING) - .withAmount("64.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset clearings require a negative amount", - "asset", - "assetClearingsNegativeAmount")); - } - - @Test - public void shouldRejectIfClearingWithZeroAmount() { - new GivenAssetValidationTestCase() - .withAnyValidDateValues() - .withAction(AssetAction.CLEARING) - .withAmount("0.00") - .when((AssetDTO assetDto) -> assetValidator.validate(assetDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Asset clearings require a negative amount", - "asset", - "assetClearingsNegativeAmount")); - } - - // -- only test fixture below --- - - private class GivenAssetValidationTestCase { - - private final AssetDTO assetDto = new AssetDTO(); - private BadRequestAlertException actualException; - - public GivenAssetValidationTestCase withId(long id) { - assetDto.setId(id); - return this; - } - - GivenAssetValidationTestCase withDocumentDate(String documentDate) { - assetDto.setDocumentDate(LocalDate.parse(documentDate)); - return this; - } - - GivenAssetValidationTestCase withValueDate(String valueDate) { - assetDto.setValueDate(LocalDate.parse(valueDate)); - return this; - } - - public GivenAssetValidationTestCase withAnyValidDateValues() { - return withDocumentDate("2019-04-11").withValueDate("2019-04-12"); - } - - GivenAssetValidationTestCase withAction(AssetAction assetAction) { - assetDto.setAction(assetAction); - return this; - } - - GivenAssetValidationTestCase withAmount(String amount) { - assetDto.setAmount(new BigDecimal(amount)); - return this; - } - - GivenAssetValidationTestCase when(final Consumer statement) { - actualException = catchThrowableOfType(() -> assetValidator.validate(assetDto), BadRequestAlertException.class); - return this; - } - - public AbstractThrowableAssert thenActualException() { - return assertThat(actualException); - } - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/MailServiceIntTest.java b/src/test/java/org/hostsharing/hsadminng/service/MailServiceIntTest.java deleted file mode 100644 index 70022a1a..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/MailServiceIntTest.java +++ /dev/null @@ -1,192 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.config.Constants; -import org.hostsharing.hsadminng.domain.User; - -import io.github.jhipster.config.JHipsterProperties; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.MockitoAnnotations; -import org.mockito.Spy; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.MessageSource; -import org.springframework.mail.MailSendException; -import org.springframework.mail.javamail.JavaMailSenderImpl; -import org.springframework.test.context.junit4.SpringRunner; -import org.thymeleaf.spring5.SpringTemplateEngine; - -import java.io.ByteArrayOutputStream; - -import javax.mail.Multipart; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class MailServiceIntTest { - - @Autowired - private JHipsterProperties jHipsterProperties; - - @Autowired - private MessageSource messageSource; - - @Autowired - private SpringTemplateEngine templateEngine; - - @Spy - private JavaMailSenderImpl javaMailSender; - - @Captor - private ArgumentCaptor messageCaptor; - - private MailService mailService; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - doNothing().when(javaMailSender).send(any(MimeMessage.class)); - mailService = new MailService(jHipsterProperties, javaMailSender, messageSource, templateEngine); - } - - @Test - public void testSendEmail() throws Exception { - mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", false, false); - verify(javaMailSender).send(messageCaptor.capture()); - MimeMessage message = messageCaptor.getValue(); - assertThat(message.getSubject()).isEqualTo("testSubject"); - assertThat(message.getAllRecipients()[0].toString()).isEqualTo("john.doe@example.com"); - assertThat(message.getFrom()[0].toString()).isEqualTo("test@localhost"); - assertThat(message.getContent()).isInstanceOf(String.class); - assertThat(message.getContent().toString()).isEqualTo("testContent"); - assertThat(message.getDataHandler().getContentType()).isEqualTo("text/plain; charset=UTF-8"); - } - - @Test - public void testSendHtmlEmail() throws Exception { - mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", false, true); - verify(javaMailSender).send(messageCaptor.capture()); - MimeMessage message = messageCaptor.getValue(); - assertThat(message.getSubject()).isEqualTo("testSubject"); - assertThat(message.getAllRecipients()[0].toString()).isEqualTo("john.doe@example.com"); - assertThat(message.getFrom()[0].toString()).isEqualTo("test@localhost"); - assertThat(message.getContent()).isInstanceOf(String.class); - assertThat(message.getContent().toString()).isEqualTo("testContent"); - assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); - } - - @Test - public void testSendMultipartEmail() throws Exception { - mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", true, false); - verify(javaMailSender).send(messageCaptor.capture()); - MimeMessage message = messageCaptor.getValue(); - MimeMultipart mp = (MimeMultipart) message.getContent(); - MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) mp.getBodyPart(0).getContent()).getBodyPart(0); - ByteArrayOutputStream aos = new ByteArrayOutputStream(); - part.writeTo(aos); - assertThat(message.getSubject()).isEqualTo("testSubject"); - assertThat(message.getAllRecipients()[0].toString()).isEqualTo("john.doe@example.com"); - assertThat(message.getFrom()[0].toString()).isEqualTo("test@localhost"); - assertThat(message.getContent()).isInstanceOf(Multipart.class); - assertThat(aos.toString()).isEqualTo("\r\ntestContent"); - assertThat(part.getDataHandler().getContentType()).isEqualTo("text/plain; charset=UTF-8"); - } - - @Test - public void testSendMultipartHtmlEmail() throws Exception { - mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", true, true); - verify(javaMailSender).send(messageCaptor.capture()); - MimeMessage message = messageCaptor.getValue(); - MimeMultipart mp = (MimeMultipart) message.getContent(); - MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) mp.getBodyPart(0).getContent()).getBodyPart(0); - ByteArrayOutputStream aos = new ByteArrayOutputStream(); - part.writeTo(aos); - assertThat(message.getSubject()).isEqualTo("testSubject"); - assertThat(message.getAllRecipients()[0].toString()).isEqualTo("john.doe@example.com"); - assertThat(message.getFrom()[0].toString()).isEqualTo("test@localhost"); - assertThat(message.getContent()).isInstanceOf(Multipart.class); - assertThat(aos.toString()).isEqualTo("\r\ntestContent"); - assertThat(part.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); - } - - @Test - public void testSendEmailFromTemplate() throws Exception { - User user = new User(); - user.setLogin("john"); - user.setEmail("john.doe@example.com"); - user.setLangKey("en"); - mailService.sendEmailFromTemplate(user, "mail/testEmail", "email.test.title"); - verify(javaMailSender).send(messageCaptor.capture()); - MimeMessage message = messageCaptor.getValue(); - assertThat(message.getSubject()).isEqualTo("test title"); - assertThat(message.getAllRecipients()[0].toString()).isEqualTo(user.getEmail()); - assertThat(message.getFrom()[0].toString()).isEqualTo("test@localhost"); - assertThat(message.getContent().toString()) - .isEqualToNormalizingNewlines("test title, http://127.0.0.1:8080, john\n"); - assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); - } - - @Test - public void testSendActivationEmail() throws Exception { - User user = new User(); - user.setLangKey(Constants.DEFAULT_LANGUAGE); - user.setLogin("john"); - user.setEmail("john.doe@example.com"); - mailService.sendActivationEmail(user); - verify(javaMailSender).send(messageCaptor.capture()); - MimeMessage message = messageCaptor.getValue(); - assertThat(message.getAllRecipients()[0].toString()).isEqualTo(user.getEmail()); - assertThat(message.getFrom()[0].toString()).isEqualTo("test@localhost"); - assertThat(message.getContent().toString()).isNotEmpty(); - assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); - } - - @Test - public void testCreationEmail() throws Exception { - User user = new User(); - user.setLangKey(Constants.DEFAULT_LANGUAGE); - user.setLogin("john"); - user.setEmail("john.doe@example.com"); - mailService.sendCreationEmail(user); - verify(javaMailSender).send(messageCaptor.capture()); - MimeMessage message = messageCaptor.getValue(); - assertThat(message.getAllRecipients()[0].toString()).isEqualTo(user.getEmail()); - assertThat(message.getFrom()[0].toString()).isEqualTo("test@localhost"); - assertThat(message.getContent().toString()).isNotEmpty(); - assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); - } - - @Test - public void testSendPasswordResetMail() throws Exception { - User user = new User(); - user.setLangKey(Constants.DEFAULT_LANGUAGE); - user.setLogin("john"); - user.setEmail("john.doe@example.com"); - mailService.sendPasswordResetMail(user); - verify(javaMailSender).send(messageCaptor.capture()); - MimeMessage message = messageCaptor.getValue(); - assertThat(message.getAllRecipients()[0].toString()).isEqualTo(user.getEmail()); - assertThat(message.getFrom()[0].toString()).isEqualTo("test@localhost"); - assertThat(message.getContent().toString()).isNotEmpty(); - assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); - } - - @Test - public void testSendEmailWithException() throws Exception { - doThrow(MailSendException.class).when(javaMailSender).send(any(MimeMessage.class)); - mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", false, false); - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/MembershipServiceUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/MembershipServiceUnitTest.java deleted file mode 100644 index 1b5452d6..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/MembershipServiceUnitTest.java +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowableOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.service.dto.MembershipDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.apache.commons.lang3.RandomUtils; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -public class MembershipServiceUnitTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private MembershipRepository membershipRepository; - - @Mock - private MembershipValidator membershipValidator; - - @InjectMocks - private MembershipService membershipService; - - @Test - public void deleteIsRejectedForMembership() { - // when - final Throwable throwException = catchThrowableOfType( - () -> membershipService.delete(RandomUtils.nextLong()), - BadRequestAlertException.class); - - // then - assertThat(throwException).isEqualToComparingFieldByField( - new BadRequestAlertException("Membership cannot be deleted", "membership", "membershipNotDeletable")); - } - - @Test - public void saveRejectsInvalidMembershipDTO() { - // given - final MembershipDTO givenMembershipDTO = new MembershipDTO(); - final BadRequestAlertException givenBadRequestAlertException = new BadRequestAlertException( - "Invalid Membership", - Membership.ENTITY_NAME, - "invalidMembership"); - doThrow(givenBadRequestAlertException).when(membershipValidator).validate(givenMembershipDTO); - - // when - final Throwable throwException = catchThrowableOfType( - () -> membershipService.save(givenMembershipDTO), - BadRequestAlertException.class); - - // then - assertThat(throwException).isSameAs(givenBadRequestAlertException); - verify(membershipRepository, never()).save(any(Membership.class)); - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/MembershipValidatorUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/MembershipValidatorUnitTest.java deleted file mode 100644 index a1327412..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/MembershipValidatorUnitTest.java +++ /dev/null @@ -1,149 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowableOfType; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.service.dto.MembershipDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.assertj.core.api.AbstractThrowableAssert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.time.LocalDate; -import java.util.function.Consumer; - -public class MembershipValidatorUnitTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private MembershipRepository membershipRepository; - - @InjectMocks - private MembershipValidator membershipValidator; - - @Before - public void initMocks() { - given(membershipRepository.hasUncancelledMembershipForCustomer(anyLong())).willReturn(false); - } - - @Test - public void shouldAcceptNewMembershipIfUntilDateAfterSinceDate() { - new GivenMembershipValidationTestCase() - .withNewMembershipForCustomer(1L) - .since("2019-04-11") - .until("2019-04-12") - .when((MembershipDTO membershipDto) -> membershipValidator.validate(membershipDto)) - .thenActualException() - .isNull(); - } - - @Test - public void shouldRejectNewMembershipIfUntilDateEqualToSinceDate() { - new GivenMembershipValidationTestCase() - .withNewMembershipForCustomer(1L) - .since("2019-04-11") - .until("2019-04-11") - .when((MembershipDTO membershipDto) -> membershipValidator.validate(membershipDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Invalid untilDate", - "membership", - "untilDateMustBeAfterSinceDate")); - } - - @Test - public void shouldRejectNewMembershipIfUntilDateAfterSinceDate() { - new GivenMembershipValidationTestCase() - .withNewMembershipForCustomer(1L) - .since("2019-04-12") - .until("2019-04-11") - .when((MembershipDTO membershipDto) -> membershipValidator.validate(membershipDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Invalid untilDate", - "membership", - "untilDateMustBeAfterSinceDate")); - } - - @Test - public void shouldAcceptNewUncancelledMembershipIfNoUncancelledMembershipExistsForSameCustomer() { - new GivenMembershipValidationTestCase() - .withUncancelledMembershipForCustomer(1L, false) - .withNewMembershipForCustomer(1L) - .since("2019-04-12") - .when((MembershipDTO membershipDto) -> membershipValidator.validate(membershipDto)) - .thenActualException() - .isNull(); - } - - @Test - public void shouldRejectNewMembershipIfAnyUncancelledMembershipExistsForSameCustomer() { - - new GivenMembershipValidationTestCase() - .withUncancelledMembershipForCustomer(1L, true) - .withNewMembershipForCustomer(1L) - .since("2019-04-12") - .when((MembershipDTO membershipDto) -> membershipValidator.validate(membershipDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Another uncancelled membership exists", - "membership", - "anotherUncancelledMembershipExists")); - } - - // -- only test fixture below --- - - private class GivenMembershipValidationTestCase { - - private final MembershipDTO membershipDto = new MembershipDTO(); - private BadRequestAlertException actualException; - - GivenMembershipValidationTestCase withUncancelledMembershipForCustomer( - final long customerId, - final boolean hasUncancelledMembership) { - given(membershipRepository.hasUncancelledMembershipForCustomer(customerId)).willReturn(hasUncancelledMembership); - return this; - } - - GivenMembershipValidationTestCase withNewMembershipForCustomer(long customerId) { - membershipDto.setCustomerId(1L); - return this; - } - - GivenMembershipValidationTestCase since(final String sinceDate) { - membershipDto.setMemberFromDate(LocalDate.parse(sinceDate)); - return this; - } - - public GivenMembershipValidationTestCase until(final String untilDate) { - membershipDto.setMemberUntilDate(LocalDate.parse(untilDate)); - return this; - } - - GivenMembershipValidationTestCase when(final Consumer statement) { - actualException = catchThrowableOfType( - () -> membershipValidator.validate(membershipDto), - BadRequestAlertException.class); - return this; - } - - public AbstractThrowableAssert thenActualException() { - return assertThat(actualException); - } - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/ShareServiceUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/ShareServiceUnitTest.java deleted file mode 100644 index 3d44236f..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/ShareServiceUnitTest.java +++ /dev/null @@ -1,178 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowableOfType; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.*; - -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; -import org.hostsharing.hsadminng.repository.ShareRepository; -import org.hostsharing.hsadminng.service.dto.ShareDTO; -import org.hostsharing.hsadminng.service.mapper.ShareMapper; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.apache.commons.lang3.RandomUtils; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import javax.persistence.EntityManager; - -// HINT: In IntelliJ IDEA such unit test classes can be created with Shift-Ctrl-T. -// Do not forget to amend the class name (.e.g. ...UnitTest / ...IntTest)! -public class ShareServiceUnitTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private EntityManager em; - - @Mock - private ShareRepository shareRepository; - - @Mock - private ShareValidator shareValidator; // needed for @InjectMocks shareService - - @Mock - private ShareMapper shareMapper; - - @InjectMocks - private ShareService shareService; - - // HINT: Click outside of any test method (e.g. here) and use Ctrl-Shift-F10 - // to run all tests from this test class. Use Ctrl-F5 to run the last execution again; - // 'execution' here can also apply to running the application, whatever ran last. - - // HINT: In IntelliJ IDEA such test methods can be created with Alt-Insert. - @Test - public void deleteIsRejectedForShareTransactions() { - // when - final Throwable throwException = catchThrowableOfType( - () -> shareService.delete(RandomUtils.nextLong()), - BadRequestAlertException.class); - - // then - // HINT: When using auto-import for assertions (e.g. via Alt-Enter in IntelliJ IDEA), - // beware to use the correct candidate from org.assertj.core.api.Assertions. - assertThat(throwException).isEqualToComparingFieldByField( - new BadRequestAlertException("Share transactions are immutable", "share", "shareTransactionImmutable")); - } - - @Test - public void saveShouldPersistValidTransactions() { - // given - final ShareDTO givenShareDTO = givenShareDTO(null, ShareAction.SUBSCRIPTION, anyPositiveNumber()); - // HINT: given(...)...will...() can't be used for void methods, in that case use Mockito's do...() methods - doNothing().when(shareValidator).validate(givenShareDTO); - - // when - final ShareDTO returnedShareDto = shareService.save(givenShareDTO); - - // then - verify(em).flush(); - verify(em).refresh(any(Share.class)); - assertThat(returnedShareDto).isEqualToIgnoringGivenFields(givenShareDTO, "id"); - } - - @Test - public void saveShouldNotPersistInvalidTransactions() { - // given - final ShareDTO givenShareDTO = givenShareDTO(null, ShareAction.SUBSCRIPTION, anyNegativeNumber()); - doThrow(new BadRequestAlertException("Some Dummy Test Violation", "share", "shareInvalidTestDummy")) - .when(shareValidator) - .validate(givenShareDTO); - - // when - final Throwable throwException = catchThrowableOfType( - () -> shareService.save(givenShareDTO), - BadRequestAlertException.class); - - // then - assertThat(throwException).isEqualToComparingFieldByField( - new BadRequestAlertException("Some Dummy Test Violation", "share", "shareInvalidTestDummy")); - } - - @Test - public void saveShouldUpdateValidTransactions() { - // given - final ShareDTO givenShareDTO = givenShareDTO(anyNonNullId(), ShareAction.SUBSCRIPTION, anyPositiveNumber()); - doNothing().when(shareValidator).validate(givenShareDTO); - - // when - final ShareDTO returnedShareDto = shareService.save(givenShareDTO); - - // then - verify(em).flush(); - verify(em).refresh(any(Share.class)); - assertThat(returnedShareDto).isEqualToIgnoringGivenFields(givenShareDTO, "id"); - } - - @Test - public void saveShouldNotUpdateInvalidTransactions() { - // given - final ShareDTO givenShareDTO = givenShareDTO(anyNonNullId(), ShareAction.SUBSCRIPTION, anyNegativeNumber()); - // HINT: given(...) can't be used for void methods, in that case use Mockito's do...() methods - doThrow(new BadRequestAlertException("Some Dummy Test Violation", "share", "shareInvalidTestDummy")) - .when(shareValidator) - .validate(givenShareDTO); - - // when - final Throwable throwException = catchThrowableOfType( - () -> shareService.save(givenShareDTO), - BadRequestAlertException.class); - - // then - assertThat(throwException).isEqualToComparingFieldByField( - new BadRequestAlertException("Some Dummy Test Violation", "share", "shareInvalidTestDummy")); - } - - // --- only test fixture code below --- - - private long anyNonNullId() { - return RandomUtils.nextInt(); - } - - // HINT: This rather complicated setup indicates that the method ShareService::save breaks the single responsibility - // principle. - private ShareDTO givenShareDTO(final Long id, final ShareAction givenAction, final int givenQuantity) { - final ShareDTO givenShareDTO = createShareDTO(id, givenAction, givenQuantity); - - // dto -> entity - final Share givenShareEntity = Mockito.mock(Share.class); - given(shareMapper.toEntity(same(givenShareDTO))).willReturn(givenShareEntity); - - // shareRepository.save(entity); - final Share persistedShareEntity = Mockito.mock(Share.class); - given(shareRepository.save(same(givenShareEntity))).willReturn(persistedShareEntity); - - // entity -> dto - ShareDTO persistedShareDTO = createShareDTO(id == null ? RandomUtils.nextLong() : id, givenAction, givenQuantity); - given(shareMapper.toDto(same(persistedShareEntity))).willReturn(persistedShareDTO); - - return givenShareDTO; - } - - private ShareDTO createShareDTO(Long id, ShareAction givenAction, int givenQuantity) { - final ShareDTO givenShareDTO = new ShareDTO(); - givenShareDTO.setId(id); - givenShareDTO.setAction(givenAction); - givenShareDTO.setQuantity(givenQuantity); - return givenShareDTO; - } - - private int anyPositiveNumber() { - return RandomUtils.nextInt(1, 1000); - } - - private int anyNegativeNumber() { - return -anyPositiveNumber(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/ShareValidatorUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/ShareValidatorUnitTest.java deleted file mode 100644 index 6f8993b7..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/ShareValidatorUnitTest.java +++ /dev/null @@ -1,190 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowableOfType; - -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; -import org.hostsharing.hsadminng.service.dto.ShareDTO; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.apache.commons.lang3.RandomUtils; -import org.assertj.core.api.AbstractThrowableAssert; -import org.junit.Test; - -import java.time.LocalDate; -import java.util.function.Consumer; - -public class ShareValidatorUnitTest { - - private ShareValidator shareValidator = new ShareValidator(); - - @Test - public void shouldAcceptValidSubscription() { - new GivenShareValidationTestCase() - .withAnyValidDateValues() - .withAction(ShareAction.SUBSCRIPTION) - .withQuantity(1) - .when((ShareDTO shareDto) -> shareValidator.validate(shareDto)) - .thenActualException() - .isNull(); - } - - @Test - public void shouldAcceptValidCancellation() { - new GivenShareValidationTestCase() - .withAnyValidDateValues() - .withAction(ShareAction.CANCELLATION) - .withQuantity(-1) - .when((ShareDTO shareDto) -> shareValidator.validate(shareDto)) - .thenActualException() - .isNull(); - } - - @Test - public void shouldAcceptIfDocumentDateEqualsValueDate() { - new GivenShareValidationTestCase() - .withDocumentDate("2019-04-11") - .withValueDate("2019-04-11") - .withAction(ShareAction.SUBSCRIPTION) - .withQuantity(1) - .when((ShareDTO shareDto) -> shareValidator.validate(shareDto)) - .thenActualException() - .isNull(); - } - - @Test - public void shouldRejectUpdates() { - new GivenShareValidationTestCase() - .withId(RandomUtils.nextLong()) - .when((ShareDTO shareDto) -> shareValidator.validate(shareDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Share transactions are immutable", - "share", - "shareTransactionImmutable")); - } - - @Test - public void shouldRejectIfDocumentDateAfterValueDate() { - new GivenShareValidationTestCase() - .withDocumentDate("2019-04-13") - .withValueDate("2019-04-12") - .withAction(ShareAction.SUBSCRIPTION) - .withQuantity(1) - .when((ShareDTO shareDto) -> shareValidator.validate(shareDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Document date may not be after value date", - "share", - "documentDateMayNotBeAfterValueDate")); - } - - @Test - public void shouldRejectIfSubscriptionWithNegativeQuantity() { - new GivenShareValidationTestCase() - .withAnyValidDateValues() - .withAction(ShareAction.SUBSCRIPTION) - .withQuantity(-1) - .when((ShareDTO shareDto) -> shareValidator.validate(shareDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Share subscriptions require a positive quantity", - "share", - "shareSubscriptionPositiveQuantity")); - } - - @Test - public void shouldRejectIfSubscriptionWithZeroQuantity() { - new GivenShareValidationTestCase() - .withAnyValidDateValues() - .withAction(ShareAction.SUBSCRIPTION) - .withQuantity(0) - .when((ShareDTO shareDto) -> shareValidator.validate(shareDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Share subscriptions require a positive quantity", - "share", - "shareSubscriptionPositiveQuantity")); - } - - @Test - public void shouldRejectIfCancellationWithPositiveQuantity() { - new GivenShareValidationTestCase() - .withAnyValidDateValues() - .withAction(ShareAction.CANCELLATION) - .withQuantity(1) - .when((ShareDTO shareDto) -> shareValidator.validate(shareDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Share cancellations require a negative quantity", - "share", - "shareCancellationNegativeQuantity")); - } - - @Test - public void shouldRejectIfCancellationWithZeroQuantity() { - new GivenShareValidationTestCase() - .withAnyValidDateValues() - .withAction(ShareAction.CANCELLATION) - .withQuantity(0) - .when((ShareDTO shareDto) -> shareValidator.validate(shareDto)) - .thenActualException() - .isEqualToComparingFieldByField( - new BadRequestAlertException( - "Share cancellations require a negative quantity", - "share", - "shareCancellationNegativeQuantity")); - } - - // -- only test fixture below --- - - private class GivenShareValidationTestCase { - - private final ShareDTO shareDto = new ShareDTO(); - private BadRequestAlertException actualException; - - public GivenShareValidationTestCase withId(long id) { - shareDto.setId(id); - return this; - } - - GivenShareValidationTestCase withDocumentDate(String documentDate) { - shareDto.setDocumentDate(LocalDate.parse(documentDate)); - return this; - } - - GivenShareValidationTestCase withValueDate(String valueDate) { - shareDto.setValueDate(LocalDate.parse(valueDate)); - return this; - } - - public GivenShareValidationTestCase withAnyValidDateValues() { - return withDocumentDate("2019-04-11").withValueDate("2019-04-12"); - } - - GivenShareValidationTestCase withAction(ShareAction shareAction) { - shareDto.setAction(shareAction); - return this; - } - - GivenShareValidationTestCase withQuantity(Integer quantity) { - shareDto.setQuantity(quantity); - return this; - } - - GivenShareValidationTestCase when(final Consumer statement) { - actualException = catchThrowableOfType(() -> shareValidator.validate(shareDto), BadRequestAlertException.class); - return this; - } - - public AbstractThrowableAssert thenActualException() { - return assertThat(actualException); - } - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/UserRoleAssignmentServiceUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/UserRoleAssignmentServiceUnitTest.java deleted file mode 100644 index de103658..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/UserRoleAssignmentServiceUnitTest.java +++ /dev/null @@ -1,99 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.domain.UserRoleAssignment; -import org.hostsharing.hsadminng.repository.UserRoleAssignmentRepository; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerFinancialContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerTechnicalContact; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextFake; - -import com.google.common.base.VerifyException; - -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.Arrays; -import java.util.Set; - -public class UserRoleAssignmentServiceUnitTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private UserRoleAssignmentRepository userRoleAssignmentRepository; - - @InjectMocks - private UserRoleAssignmentService userRoleAssignmentService; - - @Test - public void getEffectiveRoleOfCurrentUserReturnsEmptySetIfUserNotAuthenticated() { - // when - final Set actual = userRoleAssignmentService.getEffectiveRoleOfCurrentUser("test.Something", 1L); - - // then - assertThat(actual).isEmpty(); - } - - @Test - public void getEffectiveRoleOfCurrentUserReturnsEmptySetIfUserAuthenticatedButNoRolesAssigned() { - // given - SecurityContextFake.havingAuthenticatedUser(); - - // when - final Set actual = userRoleAssignmentService.getEffectiveRoleOfCurrentUser("test.Something", 1L); - - // then - assertThat(actual).isEmpty(); - } - - @Test - public void getEffectiveRoleOfCurrentUserReturnsExactlyAssignedRoles() { - // given - final String givenUserLogin = "someUser"; - SecurityContextFake.havingAuthenticatedUser(givenUserLogin); - final long givenEntityObjectId = 2L; - final String givenEntityTypeId = "test.Something"; - given(userRoleAssignmentRepository.findByLogin(givenUserLogin)).willReturn( - Arrays.asList( - new UserRoleAssignment().entityTypeId("test.SomethingElse") - .entityObjectId(givenEntityObjectId) - .assignedRole(CustomerContractualContact.ROLE), - new UserRoleAssignment().entityTypeId(givenEntityTypeId) - .entityObjectId(givenEntityObjectId) - .assignedRole(CustomerFinancialContact.ROLE), - new UserRoleAssignment().entityTypeId(givenEntityTypeId) - .entityObjectId(givenEntityObjectId) - .assignedRole(CustomerTechnicalContact.ROLE), - new UserRoleAssignment().entityTypeId(givenEntityTypeId) - .entityObjectId(3L) - .assignedRole(CustomerContractualContact.ROLE))); - - // when - final Set actual = userRoleAssignmentService - .getEffectiveRoleOfCurrentUser(givenEntityTypeId, givenEntityObjectId); - - // then - assertThat(actual) - .containsExactlyInAnyOrder(Role.of(CustomerFinancialContact.class), Role.of(CustomerTechnicalContact.class)); - } - - @Test - public void getEffectiveRoleOfCurrentUserThrowsExceptionIfEntityTypeIdIsMissing() { - // when - final Throwable actual = catchThrowable(() -> userRoleAssignmentService.getEffectiveRoleOfCurrentUser(null, 1L)); - - // then - assertThat(actual).isInstanceOf(VerifyException.class); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/UserServiceIntTest.java b/src/test/java/org/hostsharing/hsadminng/service/UserServiceIntTest.java deleted file mode 100644 index d872bf32..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/UserServiceIntTest.java +++ /dev/null @@ -1,194 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.when; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.config.Constants; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.service.dto.UserDTO; -import org.hostsharing.hsadminng.service.util.RandomUtil; - -import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.auditing.AuditingHandler; -import org.springframework.data.auditing.DateTimeProvider; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.Optional; - -/** - * Test class for the UserResource REST controller. - * - * @see UserService - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -@Transactional -public class UserServiceIntTest { - - @Autowired - private UserRepository userRepository; - - @Autowired - private UserService userService; - - @Autowired - private AuditingHandler auditingHandler; - - @Mock - DateTimeProvider dateTimeProvider; - - private User user; - - @Before - public void init() { - user = new User(); - user.setLogin("johndoe"); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(true); - user.setEmail("johndoe@localhost"); - user.setFirstName("john"); - user.setLastName("doe"); - user.setImageUrl("http://placehold.it/50x50"); - user.setLangKey("en"); - - when(dateTimeProvider.getNow()).thenReturn(Optional.of(LocalDateTime.now())); - auditingHandler.setDateTimeProvider(dateTimeProvider); - } - - @Test - @Transactional - public void assertThatUserMustExistToResetPassword() { - userRepository.saveAndFlush(user); - Optional maybeUser = userService.requestPasswordReset("invalid.login@localhost"); - assertThat(maybeUser).isNotPresent(); - - maybeUser = userService.requestPasswordReset(user.getEmail()); - assertThat(maybeUser).isPresent(); - assertThat(maybeUser.orElse(null).getEmail()).isEqualTo(user.getEmail()); - assertThat(maybeUser.orElse(null).getResetDate()).isNotNull(); - assertThat(maybeUser.orElse(null).getResetKey()).isNotNull(); - } - - @Test - @Transactional - public void assertThatOnlyActivatedUserCanRequestPasswordReset() { - user.setActivated(false); - userRepository.saveAndFlush(user); - - Optional maybeUser = userService.requestPasswordReset(user.getLogin()); - assertThat(maybeUser).isNotPresent(); - userRepository.delete(user); - } - - @Test - @Transactional - public void assertThatResetKeyMustNotBeOlderThan24Hours() { - Instant daysAgo = Instant.now().minus(25, ChronoUnit.HOURS); - String resetKey = RandomUtil.generateResetKey(); - user.setActivated(true); - user.setResetDate(daysAgo); - user.setResetKey(resetKey); - userRepository.saveAndFlush(user); - - Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); - assertThat(maybeUser).isNotPresent(); - userRepository.delete(user); - } - - @Test - @Transactional - public void assertThatResetKeyMustBeValid() { - Instant daysAgo = Instant.now().minus(25, ChronoUnit.HOURS); - user.setActivated(true); - user.setResetDate(daysAgo); - user.setResetKey("1234"); - userRepository.saveAndFlush(user); - - Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); - assertThat(maybeUser).isNotPresent(); - userRepository.delete(user); - } - - @Test - @Transactional - public void assertThatUserCanResetPassword() { - String oldPassword = user.getPassword(); - Instant daysAgo = Instant.now().minus(2, ChronoUnit.HOURS); - String resetKey = RandomUtil.generateResetKey(); - user.setActivated(true); - user.setResetDate(daysAgo); - user.setResetKey(resetKey); - userRepository.saveAndFlush(user); - - Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); - assertThat(maybeUser).isPresent(); - assertThat(maybeUser.orElse(null).getResetDate()).isNull(); - assertThat(maybeUser.orElse(null).getResetKey()).isNull(); - assertThat(maybeUser.orElse(null).getPassword()).isNotEqualTo(oldPassword); - - userRepository.delete(user); - } - - @Test - @Transactional - public void testFindNotActivatedUsersByCreationDateBefore() { - Instant now = Instant.now(); - when(dateTimeProvider.getNow()).thenReturn(Optional.of(now.minus(4, ChronoUnit.DAYS))); - user.setActivated(false); - User dbUser = userRepository.saveAndFlush(user); - dbUser.setCreatedDate(now.minus(4, ChronoUnit.DAYS)); - userRepository.saveAndFlush(user); - List users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minus(3, ChronoUnit.DAYS)); - assertThat(users).isNotEmpty(); - userService.removeNotActivatedUsers(); - users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minus(3, ChronoUnit.DAYS)); - assertThat(users).isEmpty(); - } - - @Test - @Transactional - public void assertThatAnonymousUserIsNotGet() { - user.setLogin(Constants.ANONYMOUS_USER); - if (!userRepository.findOneByLogin(Constants.ANONYMOUS_USER).isPresent()) { - userRepository.saveAndFlush(user); - } - final PageRequest pageable = PageRequest.of(0, (int) userRepository.count()); - final Page allManagedUsers = userService.getAllManagedUsers(pageable); - assertThat( - allManagedUsers.getContent() - .stream() - .noneMatch(user -> Constants.ANONYMOUS_USER.equals(user.getLogin()))) - .isTrue(); - } - - @Test - @Transactional - public void testRemoveNotActivatedUsers() { - // custom "now" for audit to use as creation date - when(dateTimeProvider.getNow()).thenReturn(Optional.of(Instant.now().minus(30, ChronoUnit.DAYS))); - - user.setActivated(false); - userRepository.saveAndFlush(user); - - assertThat(userRepository.findOneByLogin("johndoe")).isPresent(); - userService.removeNotActivatedUsers(); - assertThat(userRepository.findOneByLogin("johndoe")).isNotPresent(); - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilterTest.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilterTest.java deleted file mode 100644 index dd6a1ba1..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilterTest.java +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; - -import org.junit.Test; -import org.mockito.Mock; - -public class JSonAccessFilterTest { - - @Mock - private UserRoleAssignmentService userRoleAssignmentService; - - @Test - public void getLoginUserRoles() { - SecurityContextFake.havingUnauthenticatedUser(); - new JSonAccessFilter(null, userRoleAssignmentService, new TestEntity()) { - - { - assertThat(this.getLoginUserRoles()).hasSize(0); - } - }; - } - - private static class TestEntity implements AccessMappings { - - @Override - public Long getId() { - return null; - } - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilterTestFixture.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilterTestFixture.java deleted file mode 100644 index ecb9fc02..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilterTestFixture.java +++ /dev/null @@ -1,237 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static org.hostsharing.hsadminng.service.accessfilter.Role.*; - -import org.hostsharing.hsadminng.service.IdToDtoResolver; -import org.hostsharing.hsadminng.service.dto.FluentBuilder; - -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.RandomUtils; - -import java.math.BigDecimal; -import java.time.LocalDate; - -public class JSonAccessFilterTestFixture { - - static GivenDto createSampleDto() { - final GivenDto dto = new GivenDto(); - dto.customerId = 888L; - dto.restrictedField = RandomStringUtils.randomAlphabetic(10); - dto.openStringField = RandomStringUtils.randomAlphabetic(10); - dto.openIntegerField = RandomUtils.nextInt(); - dto.openPrimitiveIntField = RandomUtils.nextInt(); - dto.openLongField = RandomUtils.nextLong(); - dto.openPrimitiveLongField = RandomUtils.nextLong(); - dto.openBooleanField = true; - dto.openPrimitiveBooleanField = false; - dto.openBigDecimalField = new BigDecimal("987654321234567890987654321234567890.09"); - dto.openLocalDateField = LocalDate.parse("2019-04-25"); - dto.openLocalDateFieldAsString = "2019-04-25"; - dto.openEnumField = TestEnum.GREEN; - dto.openEnumFieldAsString = "GREEN"; - return dto; - } - - @EntityTypeId("test.GivenCustomer") - static class GivenCustomerDto implements FluentBuilder { - - @SelfId(resolver = GivenService.class) - @AccessFor(read = Anybody.class) - Long id; - - @AccessFor(update = Ignored.class, read = Anybody.class) - String displayLabel; - - } - - static abstract class GivenCustomerService implements IdToDtoResolver { - } - - @EntityTypeId("test.Given") - static class GivenDto implements AccessMappings, FluentBuilder { - - @SelfId(resolver = GivenService.class) - @AccessFor(read = Anybody.class) - Long id; - - @ParentId(resolver = GivenCustomerService.class) - @AccessFor(init = AnyCustomerUser.class, update = AnyCustomerUser.class, read = AnyCustomerUser.class) - Long customerId; - - @AccessFor( - init = { CustomerTechnicalContact.class, CustomerFinancialContact.class }, - update = { CustomerTechnicalContact.class, CustomerFinancialContact.class }, - read = { CustomerTechnicalContact.class, CustomerFinancialContact.class }) - String restrictedField; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - String openStringField; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - Integer openIntegerField; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - int openPrimitiveIntField; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - Long openLongField; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - long openPrimitiveLongField; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - Boolean openBooleanField; - - @AccessFor(read = Anybody.class) - boolean openPrimitiveBooleanField; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - LocalDate openLocalDateField; - transient String openLocalDateFieldAsString; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - LocalDate openLocalDateField2; - transient String openLocalDateField2AsString; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - TestEnum openEnumField; - transient String openEnumFieldAsString; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - BigDecimal openBigDecimalField; - - @AccessFor(init = Supporter.class, update = Supporter.class, read = Supporter.class) - BigDecimal restrictedBigDecimalField; - - @AccessFor(init = Anybody.class, update = Anybody.class, read = Anybody.class) - int[] openArrayField; - - @AccessFor(init = Ignored.class, update = Ignored.class, read = Anybody.class) - String displayLabel; - - @Override - public Long getId() { - return id; - } - } - - static abstract class GivenService implements IdToDtoResolver { - } - - enum TestEnum { - BLUE, - GREEN - } - - static abstract class GivenChildService implements IdToDtoResolver { - } - - public static class GivenChildDto implements AccessMappings, FluentBuilder { - - @SelfId(resolver = GivenChildService.class) - @AccessFor(read = AnyCustomerUser.class) - Long id; - - @AccessFor( - init = CustomerContractualContact.class, - update = CustomerContractualContact.class, - read = AnyCustomerUser.class) - @ParentId(resolver = GivenService.class) - Long parentId; - - @AccessFor( - init = { CustomerTechnicalContact.class, CustomerFinancialContact.class }, - update = { - CustomerTechnicalContact.class, - CustomerFinancialContact.class }) - String restrictedField; - - @Override - public Long getId() { - return id; - } - } - - public static class GivenDtoWithMultipleSelfId implements AccessMappings { - - @SelfId(resolver = GivenChildService.class) - @AccessFor(read = AnyCustomerUser.class) - Long id; - - @SelfId(resolver = GivenChildService.class) - @AccessFor(read = AnyCustomerUser.class) - Long id2; - - @Override - public Long getId() { - return id; - } - } - - public static class GivenDtoWithUnknownFieldType implements AccessMappings { - - @SelfId(resolver = GivenChildService.class) - @AccessFor(read = Anybody.class) - Long id; - - @AccessFor(init = Anybody.class, read = Anybody.class) - Arbitrary unknown; - - @Override - public Long getId() { - return id; - } - } - - static class Arbitrary { - } - - @EntityTypeId("givenParent") - public static class GivenParent implements AccessMappings, FluentBuilder { - - @SelfId(resolver = GivenParentService.class) - @AccessFor(read = AnyCustomerUser.class) - Long id; - - @Override - public Long getId() { - return id; - } - - public GivenParent id(final long id) { - this.id = id; - return this; - } - } - - public static class GivenChild implements AccessMappings, FluentBuilder { - - @SelfId(resolver = GivenChildService.class) - @AccessFor(read = AnyCustomerUser.class) - Long id; - - @AccessFor( - init = CustomerContractualContact.class, - update = CustomerContractualContact.class, - read = AnyCustomerUser.class) - @ParentId(resolver = GivenParentService.class) - GivenParent parent; - - @AccessFor( - init = { CustomerTechnicalContact.class, CustomerFinancialContact.class }, - update = { - CustomerTechnicalContact.class, - CustomerFinancialContact.class }) - String restrictedField; - - @Override - public Long getId() { - return id; - } - } - - static abstract class GivenParentService implements IdToDtoResolver { - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonBuilder.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonBuilder.java deleted file mode 100644 index e25cac6a..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonBuilder.java +++ /dev/null @@ -1,91 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.ImmutablePair; - -import java.util.List; - -public class JSonBuilder { - - private StringBuilder json = new StringBuilder(); - - @SafeVarargs - public static String asJSon(final ImmutablePair... properties) { - final StringBuilder json = new StringBuilder(); - for (ImmutablePair prop : properties) { - json.append(inQuotes(prop.left)); - json.append(": "); - if (prop.right instanceof Number) { - json.append(prop.right); - } else if (prop.right instanceof List) { - json.append(toJSonArray(prop.right)); - } else if (prop.right instanceof String && ((String) prop.right).startsWith("{\n")) { - // TODO mhoennig: find better solution for adding object nodes - json.append(prop.right); - } else { - json.append(inQuotes(prop.right)); - } - json.append(",\n"); - } - return "{\n" + json.substring(0, json.length() - 2) + "\n}"; - } - - public JSonBuilder withFieldValue(String name, String value) { - json.append(inQuotes(name)).append(":").append(value != null ? inQuotes(value) : "null").append(","); - return this; - } - - public JSonBuilder withFieldValue(String name, Number value) { - json.append(inQuotes(name)).append(":").append(value != null ? value : "null").append(","); - return this; - } - - public JSonBuilder toJSonNullFieldDefinition(String name) { - json.append(inQuotes(name)).append(":null,"); - return this; - } - - public JSonBuilder withFieldValueIfPresent(String name, String value) { - if (value != null) { - json.append(inQuotes(name)).append(":").append(inQuotes(value)).append(","); - } - return this; - } - - public JSonBuilder withFieldValueIfPresent(String name, Number value) { - if (value != null) { - json.append(inQuotes(name)).append(":").append(value).append(","); - } - return this; - } - - public > JSonBuilder withFieldValueIfPresent(final String name, final Role value) { - if (value != null) { - json.append(inQuotes(name)).append(":").append(inQuotes(value.name())).append(","); - } - return this; - } - - @Override - public String toString() { - return "{" + StringUtils.removeEnd(json.toString(), ",") + "}"; - } - - @SuppressWarnings("unchecked") - // currently just for the case of date values represented as arrays of integer - private static String toJSonArray(final Object value) { - final StringBuilder jsonArray = new StringBuilder("["); - for (int n = 0; n < ((List) value).size(); ++n) { - if (n > 0) { - jsonArray.append(","); - } - jsonArray.append(((List) value).get(n)); - } - return jsonArray.toString() + "]"; - } - - private static String inQuotes(Object value) { - return value != null ? "\"" + value.toString() + "\"" : "null"; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilterUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilterUnitTest.java deleted file mode 100644 index 6e3ebc56..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilterUnitTest.java +++ /dev/null @@ -1,579 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.assertj.core.api.Assumptions.assumeThat; -import static org.hostsharing.hsadminng.service.accessfilter.JSonAccessFilterTestFixture.*; -import static org.hostsharing.hsadminng.service.accessfilter.JSonBuilder.asJSon; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.Role.Admin; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.core.TreeNode; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.apache.commons.lang3.NotImplementedException; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.context.ApplicationContext; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.Arrays; -import java.util.Optional; - -@SuppressWarnings("ALL") -public class JSonDeserializationWithAccessFilterUnitTest { - - public static final String SOME_BIG_DECIMAL_AS_STRING = "5432191234888.1"; - public static final BigDecimal SOME_BIG_DECIMAL = new BigDecimal(SOME_BIG_DECIMAL_AS_STRING) - .setScale(2, BigDecimal.ROUND_HALF_UP); - public static final BigDecimal SOME_BIG_DECIMAL_WITH_ANOTHER_SCALE = new BigDecimal(SOME_BIG_DECIMAL_AS_STRING) - .setScale(5, BigDecimal.ROUND_HALF_UP); - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private ApplicationContext ctx; - - @Mock - private AutowireCapableBeanFactory autowireCapableBeanFactory; - - @Mock - private JsonParser jsonParser; - - @Mock - private ObjectCodec codec; - - @Mock - private TreeNode treeNode; - - @Mock - private UserRoleAssignmentService userRoleAssignmentService; - - @Mock - private GivenService givenService; - - @Mock - private GivenChildService givenChildService; - - @Mock - private GivenParentService givenParentService; - - @Mock - private GivenCustomerService givenCustomerService; - private SecurityContextMock securityContext; - - @Before - public void init() { - securityContext = SecurityContextMock.usingMock(userRoleAssignmentService) - .havingAuthenticatedUser() - .withRole(GivenDto.class, 1234L, Role.AnyCustomerUser.ROLE); - - given(ctx.getAutowireCapableBeanFactory()).willReturn(autowireCapableBeanFactory); - given(autowireCapableBeanFactory.createBean(GivenService.class)).willReturn(givenService); - given(givenService.findOne(1234L)).willReturn( - Optional.of( - new GivenDto() - .with(dto -> { - dto.id = 1234L; - dto.customerId = 888L; - dto.openIntegerField = 11111; - dto.openPrimitiveIntField = 2222; - dto.openLongField = 33333333333333L; - dto.openPrimitiveLongField = 44444444L; - dto.openBooleanField = true; - dto.openPrimitiveBooleanField = false; - dto.openBigDecimalField = SOME_BIG_DECIMAL; - dto.openStringField = "3333"; - dto.restrictedField = "initial value of restricted field"; - dto.restrictedBigDecimalField = SOME_BIG_DECIMAL; - }))); - given(autowireCapableBeanFactory.createBean(GivenCustomerService.class)).willReturn(givenCustomerService); - given(givenCustomerService.findOne(888L)).willReturn( - Optional.of( - new GivenCustomerDto() - .with(dto -> dto.id = 888L))); - - given(autowireCapableBeanFactory.createBean(GivenChildService.class)).willReturn(givenChildService); - given(autowireCapableBeanFactory.createBean(GivenParentService.class)).willReturn(givenParentService); - - given(jsonParser.getCodec()).willReturn(codec); - } - - @Test - public void shouldDeserializeNullField() throws IOException { - // given - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("openStringField", null))); - - // when - final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null); - - // then - assertThat(actualDto.openStringField).isNull(); - } - - @Test - public void shouldDeserializeStringField() throws IOException { - // given - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("openStringField", "String Value"))); - - // when - final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null); - - // then - assertThat(actualDto.openStringField).isEqualTo("String Value"); - } - - @Test - public void shouldDeserializeIntegerField() throws IOException { - // given - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("openIntegerField", 1234))); - - // when - // @formatter:off - final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null);; - // @formatter:on - - // then - assertThat(actualDto.openIntegerField).isEqualTo(1234); - } - - @Test - public void shouldDeserializeRestrictedBigDecimalFieldIfUnchangedByCompareTo() throws IOException { - // given - assumeThat(SOME_BIG_DECIMAL_WITH_ANOTHER_SCALE).isNotEqualTo(SOME_BIG_DECIMAL); - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("restrictedBigDecimalField", SOME_BIG_DECIMAL_WITH_ANOTHER_SCALE))); - - // when - final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null); - ; - - // then - assertThat(actualDto.restrictedBigDecimalField).isEqualByComparingTo(SOME_BIG_DECIMAL); - assertThat(actualDto.restrictedBigDecimalField).isEqualByComparingTo(SOME_BIG_DECIMAL_WITH_ANOTHER_SCALE); - } - - @Test - // TODO: split in separate tests for each type, you see all errors at once (if any) and it's easier to debug when there are - // problems - public void shouldDeserializeAcessibleFieldOfAnyType() throws IOException { - // given - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("openIntegerField", 11), - ImmutablePair.of("openPrimitiveIntField", 22), - ImmutablePair.of("openLongField", 333333333333333333L), - ImmutablePair.of("openPrimitiveLongField", 44444L), - ImmutablePair.of("openBooleanField", true), - ImmutablePair.of("openPrimitiveBooleanField", false), - // TODO: ImmutablePair.of("openBigDecimalField", new BigDecimal("99999999999999999999.1")), - // check why DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS is not working! - ImmutablePair.of("openBigDecimalField", new BigDecimal("99999999999999.1")), - ImmutablePair.of("openLocalDateField", LocalDate.parse("2019-04-25")), - ImmutablePair.of("openLocalDateField2", Arrays.asList(2019, 4, 24)), - ImmutablePair.of("openEnumField", TestEnum.GREEN))); - - // when - final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null); - ; - - // then - assertThat(actualDto.openIntegerField).isEqualTo(11); - assertThat(actualDto.openPrimitiveIntField).isEqualTo(22); - assertThat(actualDto.openLongField).isEqualTo(333333333333333333L); - assertThat(actualDto.openPrimitiveLongField).isEqualTo(44444L); - assertThat(actualDto.openBooleanField).isEqualTo(true); - assertThat(actualDto.openPrimitiveBooleanField).isEqualTo(false); - assertThat(actualDto.openBigDecimalField).isEqualTo(new BigDecimal("99999999999999.1")); - assertThat(actualDto.openLocalDateField).isEqualTo(LocalDate.parse("2019-04-25")); - assertThat(actualDto.openLocalDateField2).isEqualTo(LocalDate.parse("2019-04-24")); - assertThat(actualDto.openEnumField).isEqualTo(TestEnum.GREEN); - } - - @Test - public void shouldNotDeserializeFieldWithUnknownJSonNodeType() throws IOException { - // given - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("openArrayField", Arrays.asList(11, 22, 33)))); - - // when - Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOf(NotImplementedException.class); - } - - @Test - public void shouldDeserializeStringFieldIfRequiredRoleIsCoveredByUser() throws IOException { - // given - securityContext.havingAuthenticatedUser() - .withRole(GivenCustomerDto.class, 888L, Role.CustomerFinancialContact.ROLE); - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("restrictedField", "update value of restricted field"))); - - // when - final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null); - - // then - assertThat(actualDto.restrictedField).isEqualTo("update value of restricted field"); - } - - @Test - public void shouldDeserializeUnchangedStringFieldIfRequiredRoleIsNotCoveredByUser() throws IOException { - // given - securityContext.havingAuthenticatedUser() - .withRole(GivenCustomerDto.class, 888L, Role.CustomerFinancialContact.ROLE); - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("restrictedField", "initial value of restricted field"))); - - // when - final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null); - - // then - assertThat(actualDto.restrictedField).isEqualTo("initial value of restricted field"); - } - - @Test - public void shouldNotDeserializeUpatedStringFieldIfRequiredRoleIsNotCoveredByUser() throws IOException { - // given - securityContext.havingAuthenticatedUser() - .withRole(GivenCustomerDto.class, 888L, Role.ActualCustomerUser.ROLE); - givenJSonTree( - asJSon( - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("restrictedField", "updated value of restricted field"))); - - // when - final Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { - assertThat(badRequestAlertException.getParam()).isEqualTo("GivenDto.restrictedField"); - assertThat(badRequestAlertException.getErrorKey()).isEqualTo("initializationProhibited"); - }); - } - - @Test - public void shouldNotInitializeFieldIfRequiredRoleIsNotCoveredByUser() throws IOException { - // given - securityContext.havingAuthenticatedUser() - .withRole(GivenCustomerDto.class, 888L, Role.ActualCustomerUser.ROLE); - givenJSonTree( - asJSon( - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("restrictedField", "another value of restricted field"))); - - // when - final Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { - assertThat(badRequestAlertException.getParam()).isEqualTo("GivenDto.restrictedField"); - assertThat(badRequestAlertException.getErrorKey()).isEqualTo("initializationProhibited"); - }); - } - - @Test - public void shouldNotCreateIfRoleRequiredByParentEntityIsNotCoveredByUser() throws IOException { - // given - securityContext.havingAuthenticatedUser() - .withRole(GivenCustomerDto.class, 9999L, Role.CustomerContractualContact.ROLE); - givenJSonTree( - asJSon( - ImmutablePair.of("parentId", 1234L))); - - // when - Throwable exception = catchThrowable( - () -> deserializerFor(GivenChildDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { - assertThat(badRequestAlertException.getParam()).isEqualTo("GivenChildDto.parentId"); - assertThat(badRequestAlertException.getErrorKey()).isEqualTo("referencingProhibited"); - }); - } - - @Test - public void shouldCreateIfRoleRequiredByReferencedEntityIsCoveredByUser() throws IOException { - // given - securityContext.havingAuthenticatedUser() - .withRole(GivenCustomerDto.class, 888L, Role.CustomerContractualContact.ROLE); - givenJSonTree( - asJSon( - ImmutablePair.of("parentId", 1234L))); - - // when - final GivenChildDto actualDto = deserializerFor(GivenChildDto.class).deserialize(jsonParser, null); - ; - - // then - assertThat(actualDto.parentId).isEqualTo(1234L); - } - - @Test - public void shouldResolveParentIdFromIdOfSerializedSubEntity() throws IOException { - // given - securityContext.havingAuthenticatedUser() - .withRole(GivenParent.class, 1234L, Role.CustomerContractualContact.ROLE); - givenJSonTree( - asJSon( - ImmutablePair.of( - "parent", - asJSon( - ImmutablePair.of("id", 1234L))))); - given(givenParentService.findOne(1234L)).willReturn(Optional.of(new GivenParent().id(1234))); - - // when - final GivenChild actualDto = deserializerFor(GivenChild.class).deserialize(jsonParser, null); - - // then - assertThat(actualDto.parent.id).isEqualTo(1234L); - } - - @Test - public void shouldNotUpdateFieldIfRequiredRoleIsNotCoveredByUser() throws IOException { - // given - securityContext.havingAuthenticatedUser() - .withRole(GivenCustomerDto.class, 888L, Role.ActualCustomerUser.ROLE); - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("customerId", 888L), - ImmutablePair.of("restrictedField", "Restricted String Value"))); - - // when - final Throwable exception = catchThrowable( - () -> deserializerFor(GivenDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { - assertThat(badRequestAlertException.getParam()).isEqualTo("GivenDto.restrictedField"); - assertThat(badRequestAlertException.getErrorKey()).isEqualTo("updateProhibited"); - }); - } - - @Test - public void shouldDetectMultipleSelfIdFields() throws IOException { - // given - givenJSonTree(asJSon(ImmutablePair.of("id", 1111L))); - - // when - final Throwable exception = catchThrowable( - () -> deserializerFor(GivenDtoWithMultipleSelfId.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOf(AssertionError.class) - .hasMessage("multiple @SelfId detected in GivenDtoWithMultipleSelfId"); - } - - @Test - public void shouldDetectUnknownFieldType() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN); - givenJSonTree(asJSon(ImmutablePair.of("unknown", new Arbitrary()))); - - // when - final Throwable exception = catchThrowable( - () -> deserializerFor(GivenDtoWithUnknownFieldType.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOf(NotImplementedException.class) - .hasMessageStartingWith("property type not yet implemented: ") - .hasMessageContaining("Arbitrary") - .hasMessageContaining("GivenDtoWithUnknownFieldType.unknown"); - } - - @Test - public void shouldDetectUnknownPropertyName() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN); - givenJSonTree(asJSon(ImmutablePair.of("somePropWhichDoesNotExist", "Some Value"))); - - // when - final Throwable exception = catchThrowable( - () -> deserializerFor(GivenDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, (exc) -> { - assertThat(exc).hasMessageStartingWith("Unknown property"); - assertThat(exc.getParam()).isEqualTo("somePropWhichDoesNotExist"); - assumeThat(exc.getErrorKey()).isEqualTo("unknownProperty"); - }); - } - - @Test - public void shouldNotDeserializeArrayValue() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN); - givenJSonTree(asJSon(ImmutablePair.of("openStringField", Arrays.asList(1, 2)))); - - // when - final Throwable exception = catchThrowable( - () -> deserializerFor(GivenDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOf(NotImplementedException.class); - } - - @Test - public void shouldNotDeserializeObjectValue() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN); - givenJSonTree("{ \"openStringField\": {\"a\": 1, \"b\": 2 } }"); - - // when - final Throwable exception = catchThrowable( - () -> deserializerFor(GivenDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isInstanceOf(NotImplementedException.class); - } - - @Test - public void shouldIgnorePropertyToIgnoreForInit() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(Admin.ROLE.authority()); - givenJSonTree( - asJSon( - ImmutablePair.of("displayLabel", "Some Value"))); - - // when - deserializerFor(GivenDto.class).deserialize(jsonParser, null); - final Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isNull(); - } - - @Test - public void shouldIgnorePropertyToIgnoreForUpdate() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN); - givenJSonTree( - asJSon( - ImmutablePair.of("id", 1234L), - ImmutablePair.of("displayLabel", "Some Value"))); - - // when - final Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null)); - - // then - assertThat(exception).isNull(); - } - - // --- only fixture code below --- - - private void givenJSonTree(String givenJSon) throws IOException { - final ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); - given(codec.readTree(jsonParser)).willReturn(new ObjectMapper().readTree(givenJSon)); - } - - // We need specialied factories for the deserializer subclasses - // so that the generic type can be accessed via reflection. - // And it's down here to keep the ugly formatting out of the test cases. - // The trick with the unused ...-parameterr might look strange but the reason is - // that I wanted the concrete class to be navigable in the tests and - // multiple deserializer(Class<...Dto>) methods would have the same erasure - // and adding the type the method name, is redundant for the reader. - - public JsonDeserializerWithAccessFilter deserializerFor( - final Class clazz, - final GivenDto... qualifier) { - return new JsonDeserializerWithAccessFilter(ctx, userRoleAssignmentService) { - // no need to overload any method here - }; - } - - public JsonDeserializerWithAccessFilter deserializerFor( - final Class clazz, - final GivenChildDto... qualifier) { - return new JsonDeserializerWithAccessFilter(ctx, userRoleAssignmentService) { - // no need to overload any method here - }; - } - - private JsonDeserializer deserializerFor( - final Class clazz, - final GivenDtoWithMultipleSelfId... qualifier) { - return new JsonDeserializerWithAccessFilter(ctx, userRoleAssignmentService) { - // no need to overload any method here - }; - } - - private JsonDeserializer deserializerFor( - final Class clazz, - final GivenDtoWithUnknownFieldType... qualifier) { - return new JsonDeserializerWithAccessFilter(ctx, userRoleAssignmentService) { - // no need to overload any method here - }; - } - - public JsonDeserializerWithAccessFilter deserializerFor( - final Class clazz, - final GivenChild... qualifier) { - return new JsonDeserializerWithAccessFilter(ctx, userRoleAssignmentService) { - - @Override - protected JSonFieldReader jsonFieldReader(final TreeNode treeNode, final Field field) { - if ("parent".equals(field.getName())) { - return (final GivenChild target) -> { - final long id = getSubNode(treeNode, "id").asLong(); - target.parent = givenParentService.findOne(id) - .orElseThrow( - () -> new BadRequestAlertException( - GivenParent.class.getSimpleName() + "#" + id + " not found", - String.valueOf(id), - "idNotFound")); - }; - } - return super.jsonFieldReader(treeNode, field); - } - }; - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilterUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilterUnitTest.java deleted file mode 100644 index a7f807b3..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilterUnitTest.java +++ /dev/null @@ -1,218 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.hostsharing.hsadminng.service.accessfilter.JSonAccessFilterTestFixture.*; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; - -import com.fasterxml.jackson.core.JsonGenerator; - -import org.apache.commons.lang3.NotImplementedException; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.context.ApplicationContext; - -import java.io.IOException; -import java.util.Optional; - -public class JSonSerializationWithAccessFilterUnitTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private ApplicationContext ctx; - - @Mock - private AutowireCapableBeanFactory autowireCapableBeanFactory; - - @Mock - private JsonGenerator jsonGenerator; - - @Mock - private UserRoleAssignmentService userRoleAssignmentService; - - @Mock - private GivenCustomerService givenCustomerService; - - private SecurityContextMock securityContext; - - private final GivenDto givenDTO = createSampleDto(); - - @Before - public void init() { - securityContext = SecurityContextMock.usingMock(userRoleAssignmentService) - .havingAuthenticatedUser() - .withRole(GivenCustomerDto.class, 888L, Role.AnyCustomerUser.ROLE); - - given(ctx.getAutowireCapableBeanFactory()).willReturn(autowireCapableBeanFactory); - given(autowireCapableBeanFactory.createBean(GivenCustomerService.class)).willReturn(givenCustomerService); - given(givenCustomerService.findOne(888L)).willReturn( - Optional.of( - new GivenCustomerDto() - .with(dto -> dto.id = 888L))); - } - - @Test - public void shouldSerializeStringField() throws IOException { - // when - serialize(givenDTO); - - // then - verify(jsonGenerator).writeStringField("openStringField", givenDTO.openStringField); - } - - @Test - public void shouldSerializeIntegerField() throws IOException { - // when - serialize(givenDTO); - - // then - verify(jsonGenerator).writeNumberField("openIntegerField", givenDTO.openIntegerField); - } - - @Test - public void shouldSerializePrimitiveIntField() throws IOException { - // when - serialize(givenDTO); - - // then - verify(jsonGenerator).writeNumberField("openPrimitiveIntField", givenDTO.openPrimitiveIntField); - } - - @Test - public void shouldSerializeLongField() throws IOException { - // when - final Throwable actual = catchThrowable(() -> serialize(givenDTO)); - - // then - verify(jsonGenerator).writeNumberField("openLongField", givenDTO.openLongField); - } - - @Test - public void shouldSerializePrimitiveLongField() throws IOException { - // when - serialize(givenDTO); - - // then - verify(jsonGenerator).writeNumberField("openPrimitiveLongField", givenDTO.openPrimitiveLongField); - } - - @Test - public void shouldSerializeBooleanField() throws IOException { - // when - serialize(givenDTO); - - // then - verify(jsonGenerator).writeBooleanField("openBooleanField", givenDTO.openBooleanField); - } - - @Test - public void shouldSerializePrimitiveBooleanField() throws IOException { - // when - serialize(givenDTO); - - // then - verify(jsonGenerator).writeBooleanField("openPrimitiveBooleanField", givenDTO.openPrimitiveBooleanField); - } - - @Test - public void shouldSerializeBigDecimalField() throws IOException { - // when - final Throwable actual = catchThrowable(() -> serialize(givenDTO)); - - // then - verify(jsonGenerator).writeNumberField("openBigDecimalField", givenDTO.openBigDecimalField); - } - - @Test - public void shouldSerializeLocalDateField() throws IOException { - // when - serialize(givenDTO); - - // then - verify(jsonGenerator).writeStringField("openLocalDateField", givenDTO.openLocalDateFieldAsString); - } - - @Test - public void shouldSerializeEnumField() throws IOException { - // when - serialize(givenDTO); - - // then - verify(jsonGenerator).writeStringField("openEnumField", givenDTO.openEnumFieldAsString); - } - - @Test - public void shouldSerializeRestrictedFieldIfRequiredRoleIsCoveredByUser() throws IOException { - - // given - securityContext.havingAuthenticatedUser() - .withRole(GivenCustomerDto.class, 888L, Role.of(Role.CustomerFinancialContact.class)); - - // when - serialize(givenDTO); - - // then - verify(jsonGenerator).writeStringField("restrictedField", givenDTO.restrictedField); - } - - @Test - public void shouldNotSerializeRestrictedFieldIfRequiredRoleIsNotCoveredByUser() throws IOException { - - // given - securityContext.havingAuthenticatedUser().withRole(GivenCustomerDto.class, 888L, Role.AnyCustomerUser.ROLE); - - // when - serialize(givenDTO); - - // then - verify(jsonGenerator, never()).writeStringField("restrictedField", givenDTO.restrictedField); - } - - @Test - public void shouldThrowExceptionForUnimplementedFieldType() { - - // given - class Arbitrary { - - } - class GivenDtoWithUnimplementedFieldType implements AccessMappings { - - @AccessFor(read = Role.Anybody.class) - Arbitrary fieldWithUnimplementedType = new Arbitrary(); - - @Override - public Long getId() { - return null; - } - } - final GivenDtoWithUnimplementedFieldType givenDtoWithUnimplementedFieldType = new GivenDtoWithUnimplementedFieldType(); - SecurityContextFake.havingAuthenticatedUser(); - - // when - final Throwable actual = catchThrowable(() -> serialize(givenDtoWithUnimplementedFieldType)); - - // then - assertThat(actual).isInstanceOf(NotImplementedException.class); - } - - // --- fixture code below --- - - private void serialize(final T dto) throws IOException { - // @formatter:off - new JsonSerializerWithAccessFilter(ctx, userRoleAssignmentService) {} - .serialize(dto, jsonGenerator, null); - // @formatter:on - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/RoleUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/RoleUnitTest.java deleted file mode 100644 index aa080e22..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/RoleUnitTest.java +++ /dev/null @@ -1,195 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.ThrowableAssert.catchThrowable; - -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.accessfilter.Role.*; - -import com.google.common.base.VerifyException; - -import org.junit.Test; - -import java.lang.reflect.Field; - -public class RoleUnitTest { - - @Test - public void allUserRolesShouldCoverSameRequiredRole() { - assertThat(Hostmaster.ROLE.covers(Hostmaster.class)).isTrue(); - assertThat(Admin.ROLE.covers(Admin.class)).isTrue(); - assertThat(Supporter.ROLE.covers(Supporter.class)).isTrue(); - - assertThat(Role.CustomerContractualContact.ROLE.covers(Role.CustomerContractualContact.class)).isTrue(); - assertThat(CustomerFinancialContact.ROLE.covers(CustomerFinancialContact.class)).isTrue(); - assertThat(CustomerTechnicalContact.ROLE.covers(CustomerTechnicalContact.class)).isTrue(); - - assertThat(ActualCustomerUser.ROLE.covers((ActualCustomerUser.class))).isTrue(); - assertThat(AnyCustomerUser.ROLE.covers((Role.AnyCustomerUser.class))).isTrue(); - } - - @Test - public void lowerUserRolesShouldNotCoverHigherRequiredRoles() { - assertThat(Hostmaster.ROLE.covers(Nobody.class)).isFalse(); - assertThat(Admin.ROLE.covers(Hostmaster.class)).isFalse(); - assertThat(Supporter.ROLE.covers(Admin.class)).isFalse(); - - assertThat(AnyCustomerContact.ROLE.covers(Supporter.class)).isFalse(); - assertThat(AnyCustomerContact.ROLE.covers(Role.CustomerContractualContact.class)).isFalse(); - assertThat(CustomerFinancialContact.ROLE.covers(Role.CustomerContractualContact.class)).isFalse(); - assertThat(CustomerFinancialContact.ROLE.covers(CustomerTechnicalContact.class)).isFalse(); - assertThat(CustomerTechnicalContact.ROLE.covers(Role.CustomerContractualContact.class)).isFalse(); - assertThat(CustomerTechnicalContact.ROLE.covers(CustomerFinancialContact.class)).isFalse(); - - assertThat(ActualCustomerUser.ROLE.covers((AnyCustomerContact.class))).isFalse(); - assertThat(ActualCustomerUser.ROLE.covers((Role.CustomerContractualContact.class))).isFalse(); - assertThat(ActualCustomerUser.ROLE.covers((CustomerTechnicalContact.class))).isFalse(); - assertThat(ActualCustomerUser.ROLE.covers((CustomerFinancialContact.class))).isFalse(); - - assertThat(AnyCustomerUser.ROLE.covers((ActualCustomerUser.class))).isFalse(); - assertThat(AnyCustomerUser.ROLE.covers((AnyCustomerContact.class))).isFalse(); - assertThat(AnyCustomerUser.ROLE.covers((Role.CustomerContractualContact.class))).isFalse(); - assertThat(AnyCustomerUser.ROLE.covers((CustomerTechnicalContact.class))).isFalse(); - assertThat(AnyCustomerUser.ROLE.covers((CustomerFinancialContact.class))).isFalse(); - - assertThat(Anybody.ROLE.covers((Role.AnyCustomerUser.class))).isFalse(); - } - - @Test - public void higherUserRolesShouldCoverLowerRequiredRoles() { - assertThat(Hostmaster.ROLE.covers(Supporter.class)).isTrue(); - assertThat(Admin.ROLE.covers(Supporter.class)).isTrue(); - - assertThat(Supporter.ROLE.covers(AnyCustomerContact.class)).isTrue(); - - assertThat(Role.CustomerContractualContact.ROLE.covers(AnyCustomerContact.class)).isTrue(); - assertThat(Role.CustomerContractualContact.ROLE.covers(CustomerFinancialContact.class)).isTrue(); - assertThat(Role.CustomerContractualContact.ROLE.covers(CustomerTechnicalContact.class)).isTrue(); - assertThat(CustomerTechnicalContact.ROLE.covers(Role.AnyCustomerUser.class)).isTrue(); - - assertThat(ActualCustomerUser.ROLE.covers((Role.AnyCustomerUser.class))).isTrue(); - assertThat(AnyCustomerUser.ROLE.covers((Anybody.class))).isTrue(); - } - - @Test - public void financialContactShouldNotCoverAnyOtherRealRoleRequirement() { - assertThat(CustomerFinancialContact.ROLE.covers(Role.AnyCustomerUser.class)).isFalse(); - assertThat(CustomerFinancialContact.ROLE.covers(ActualCustomerUser.class)).isFalse(); - assertThat(CustomerFinancialContact.ROLE.covers(Role.AnyCustomerUser.class)).isFalse(); - } - - @Test - public void ignoredCoversNothingAndIsNotCovered() { - assertThat(Ignored.ROLE.covers(Hostmaster.class)).isFalse(); - assertThat(Ignored.ROLE.covers(Anybody.class)).isFalse(); - assertThat(Ignored.ROLE.covers(Ignored.class)).isFalse(); - assertThat(Hostmaster.ROLE.covers(Ignored.class)).isFalse(); - assertThat(Anybody.ROLE.covers(Ignored.class)).isFalse(); - } - - @Test - public void coversAny() { - assertThat(Hostmaster.ROLE.coversAny(Role.CustomerContractualContact.class, CustomerFinancialContact.class)).isTrue(); - assertThat( - Role.CustomerContractualContact.ROLE.coversAny( - Role.CustomerContractualContact.class, - CustomerFinancialContact.class)) - .isTrue(); - assertThat( - CustomerFinancialContact.ROLE.coversAny( - Role.CustomerContractualContact.class, - CustomerFinancialContact.class)) - .isTrue(); - - assertThat(Role.AnyCustomerUser.ROLE.coversAny(Role.CustomerContractualContact.class, CustomerFinancialContact.class)) - .isFalse(); - - assertThat(catchThrowable(Hostmaster.ROLE::coversAny)).isInstanceOf(VerifyException.class); - assertThat( - catchThrowable( - () -> Hostmaster.ROLE.coversAny( - (Class[]) null))).isInstanceOf(VerifyException.class); - } - - @Test - public void toBeIgnoredForUpdates() { - assertThat(Role.toBeIgnoredForUpdates(someFieldWithoutAccessForAnnotation)).isTrue(); - assertThat(Role.toBeIgnoredForUpdates(someFieldWithAccessForAnnotationToBeIgnoredForUpdates)).isTrue(); - assertThat(Role.toBeIgnoredForUpdates(someFieldWithAccessForAnnotationToBeIgnoredForUpdatesAmongOthers)).isFalse(); - assertThat(Role.toBeIgnoredForUpdates(someFieldWithAccessForAnnotation)).isFalse(); - } - - @Test - public void getAuthority() { - assertThat(Nobody.ROLE.authority()).isEqualTo(AuthoritiesConstants.USER); - assertThat(Hostmaster.ROLE.authority()).isEqualTo(AuthoritiesConstants.HOSTMASTER); - assertThat(Admin.ROLE.authority()).isEqualTo(AuthoritiesConstants.ADMIN); - assertThat(Supporter.ROLE.authority()).isEqualTo(AuthoritiesConstants.SUPPORTER); - assertThat(Role.CustomerContractualContact.ROLE.authority()).isEqualTo(AuthoritiesConstants.USER); - assertThat(Anybody.ROLE.authority()).isEqualTo(AuthoritiesConstants.ANONYMOUS); - } - - @Test - public void isBroadest() { - assertThat(Role.broadest(Hostmaster.ROLE, Role.CustomerContractualContact.ROLE)).isEqualTo(Hostmaster.ROLE); - assertThat(Role.broadest(Role.CustomerContractualContact.ROLE, Hostmaster.ROLE)).isEqualTo(Hostmaster.ROLE); - assertThat(Role.broadest(Role.CustomerContractualContact.ROLE, Role.AnyCustomerUser.ROLE)) - .isEqualTo(Role.CustomerContractualContact.ROLE); - } - - @Test - public void isAllowedToInit() { - assertThat(Hostmaster.ROLE.isAllowedToInit(someFieldWithoutAccessForAnnotation)).isFalse(); - assertThat(Supporter.ROLE.isAllowedToInit(someFieldWithoutAccessForAnnotation)).isFalse(); - assertThat(Admin.ROLE.isAllowedToInit(someFieldWithAccessForAnnotation)).isTrue(); - } - - @Test - public void isAllowedToUpdate() { - assertThat(Hostmaster.ROLE.isAllowedToUpdate(someFieldWithoutAccessForAnnotation)).isFalse(); - assertThat(AnyCustomerContact.ROLE.isAllowedToUpdate(someFieldWithAccessForAnnotation)).isFalse(); - assertThat(Supporter.ROLE.isAllowedToUpdate(someFieldWithAccessForAnnotation)).isTrue(); - } - - @Test - public void isAllowedToRead() { - assertThat(Hostmaster.ROLE.isAllowedToRead(someFieldWithoutAccessForAnnotation)).isFalse(); - assertThat(Role.AnyCustomerUser.ROLE.isAllowedToRead(someFieldWithAccessForAnnotation)).isFalse(); - assertThat(AnyCustomerContact.ROLE.isAllowedToRead(someFieldWithAccessForAnnotation)).isTrue(); - } - - // --- only test fixture below --- - - private static class TestDto { - - @AccessFor(init = Admin.class, update = Supporter.class, read = AnyCustomerContact.class) - private Integer someFieldWithAccessForAnnotation; - - @AccessFor(update = Ignored.class, read = AnyCustomerContact.class) - private Integer someFieldWithAccessForAnnotationToBeIgnoredForUpdates; - - @AccessFor(update = { Ignored.class, Supporter.class }, read = AnyCustomerContact.class) - private Integer someFieldWithAccessForAnnotationToBeIgnoredForUpdatesAmongOthers; - - private Integer someFieldWithoutAccessForAnnotation; - } - - private static Field someFieldWithoutAccessForAnnotation; - private static Field someFieldWithAccessForAnnotationToBeIgnoredForUpdates; - private static Field someFieldWithAccessForAnnotationToBeIgnoredForUpdatesAmongOthers; - private static Field someFieldWithAccessForAnnotation; - - static { - try { - someFieldWithoutAccessForAnnotation = TestDto.class.getDeclaredField("someFieldWithoutAccessForAnnotation"); - someFieldWithAccessForAnnotationToBeIgnoredForUpdates = TestDto.class - .getDeclaredField("someFieldWithAccessForAnnotationToBeIgnoredForUpdates"); - someFieldWithAccessForAnnotationToBeIgnoredForUpdatesAmongOthers = TestDto.class - .getDeclaredField("someFieldWithAccessForAnnotationToBeIgnoredForUpdatesAmongOthers"); - someFieldWithAccessForAnnotation = TestDto.class.getDeclaredField("someFieldWithAccessForAnnotation"); - } catch (NoSuchFieldException e) { - throw new AssertionError("precondition failed", e); - } - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextDouble.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextDouble.java deleted file mode 100644 index c88015fc..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextDouble.java +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static org.assertj.core.api.Assumptions.assumeThat; - -import org.hostsharing.hsadminng.security.SecurityUtils; - -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -import java.util.ArrayList; -import java.util.Collection; - -abstract class SecurityContextDouble { - - private final Collection authorities = new ArrayList<>(); - - protected SecurityContextDouble() { - } - - protected SecurityContextDouble withAuthenticatedUser(final String login) { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - - securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(login, "dummyPassword") { - - @Override - public Collection getAuthorities() { - return authorities; - } - }); - SecurityContextHolder.setContext(securityContext); - assumeThat(SecurityUtils.getCurrentUserLogin()).hasValue(login); - return this; - } - - public T withAuthority(final String authority) { - authorities.add((GrantedAuthority) () -> authority); - return (T) this; - } - - private static class FakePrincipal { - - private final String username; - - public FakePrincipal(final String username) { - this.username = username; - } - - @Override - public String toString() { - return username; - } - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextFake.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextFake.java deleted file mode 100644 index 85b3ef66..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextFake.java +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -public class SecurityContextFake extends SecurityContextDouble { - - public static SecurityContextFake havingUnauthenticatedUser() { - final SecurityContextFake securityContext = new SecurityContextFake(); - return securityContext; - } - - public static SecurityContextFake havingAuthenticatedUser() { - return havingAuthenticatedUser("dummyUser"); - } - - public static SecurityContextFake havingAuthenticatedUser(final String login) { - final SecurityContextFake securityContext = new SecurityContextFake(); - securityContext.withAuthenticatedUser(login); - return securityContext; - } - - protected SecurityContextFake() { - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextMock.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextMock.java deleted file mode 100644 index 402df6f8..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/SecurityContextMock.java +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.accessfilter; - -import static org.assertj.core.api.Assumptions.assumeThat; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; - -import org.mockito.Mockito; - -import java.util.Arrays; -import java.util.HashSet; - -public class SecurityContextMock extends SecurityContextDouble { - - private final UserRoleAssignmentService userRoleAssignmentService; - - public static SecurityContextMock usingMock(final UserRoleAssignmentService userRoleAssignmentService) { - return new SecurityContextMock(userRoleAssignmentService); - } - - public SecurityContextMock(final UserRoleAssignmentService userRoleAssignmentService) { - this.userRoleAssignmentService = userRoleAssignmentService; - } - - public SecurityContextMock havingAuthenticatedUser() { - return havingAuthenticatedUser("dummyUser"); - } - - public SecurityContextMock havingAuthenticatedUser(final String login) { - super.withAuthenticatedUser(login); - Mockito.reset(userRoleAssignmentService); - return this; - } - - public SecurityContextMock withRole(final Class onClass, final long onId, final Role... roles) { - if (userRoleAssignmentService == null) { - throw new IllegalStateException("mock not registered for: " + UserRoleAssignmentService.class.getSimpleName()); - } - final EntityTypeId entityTypeId = onClass.getAnnotation(EntityTypeId.class); - assumeThat(entityTypeId).as("@" + EntityTypeId.class.getSimpleName() + " missing on class " + onClass.toString()) - .isNotNull(); - given(userRoleAssignmentService.getEffectiveRoleOfCurrentUser(entityTypeId.value(), onId)) - .willReturn(new HashSet(Arrays.asList(roles))); - return this; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/AccessMappingsUnitTestBase.java b/src/test/java/org/hostsharing/hsadminng/service/dto/AccessMappingsUnitTestBase.java deleted file mode 100644 index 4d10d2c0..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/AccessMappingsUnitTestBase.java +++ /dev/null @@ -1,237 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import static org.apache.commons.lang3.StringUtils.removeEnd; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; - -import org.hostsharing.hsadminng.service.accessfilter.*; -import org.hostsharing.hsadminng.service.util.ReflectionUtil; - -import org.apache.commons.lang3.RandomUtils; -import org.junit.Test; -import org.springframework.boot.jackson.JsonComponent; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * Usually base classes for unit tests are not a good idea, but because - * DTOs which implement AccessMapping are more like a DSL, - * this base class should be used to enforce its required structure. - */ -public abstract class AccessMappingsUnitTestBase { - - private final Class dtoClass; - private final BiFunction createSampleDTO; - private final BiFunction createRandomDTO; - - public AccessMappingsUnitTestBase( - Class dtoClass, - final BiFunction createSampleDTO, - final BiFunction createRandomDTO) { - this.dtoClass = dtoClass; - this.createSampleDTO = createSampleDTO; - this.createRandomDTO = createRandomDTO; - } - - @Test - public void shouldConvertToString() { - final D sampleDto = createSampleDTO.apply(1234L, 77L); - final String dtoAsString = dtoToString(sampleDto); - assertThat(sampleDto.toString()).isEqualTo(dtoAsString); - } - - @Test - @SuppressWarnings("all") - public void shouldImplementEqualsJustUsingClassAndId() { - final D dto = createSampleDTO.apply(1234L, 77L); - assertThat(dto.equals(dto)).isTrue(); - - final D dtoWithSameId = createSampleDTO.apply(1234L, 77L); - assertThat(dto.equals(dtoWithSameId)).isTrue(); - - final D dtoWithAnotherId = createSampleDTO.apply(RandomUtils.nextLong(2000, 9999), 77L); - assertThat(dtoWithAnotherId.equals(dtoWithSameId)).isFalse(); - - final D dtoWithoutId = createSampleDTO.apply(null, RandomUtils.nextLong()); - assertThat(dto.equals(dtoWithoutId)).isFalse(); - assertThat(dtoWithoutId.equals(dto)).isFalse(); - - assertThat(dto.equals(null)).isFalse(); - assertThat(dto.equals("")).isFalse(); - } - - @Test - public void shouldImplementHashCodeJustUsingClassAndId() { - final long randomId = RandomUtils.nextLong(); - final D dto = createSampleDTO.apply(randomId, RandomUtils.nextLong()); - assertThat(dto.hashCode()).isEqualTo(Objects.hashCode(randomId)); - - final D dtoWithoutId = createSampleDTO.apply(null, RandomUtils.nextLong()); - assertThat(dtoWithoutId.hashCode()).isEqualTo(Objects.hashCode(null)); - } - - @Test - public void shouldImplementAccessMappings() { - assertThat(dtoClass.getInterfaces()).as("must implement " + AccessMappings.class).contains(AccessMappings.class); - } - - @Test - public void shouldImplementSerializer() { - shouldImplementJsonComponent(JsonSerializerWithAccessFilter.class); - } - - @Test - public void shouldImplementDeserializer() { - shouldImplementJsonComponent(JsonDeserializerWithAccessFilter.class); - } - - // --- only test fixture below --- - - protected AccessRightsMatcher initAccessFor(final Class dtoClass, final Role role) { - return new AccessRightsMatcher(dtoClass, role, AccessFor::init); - } - - protected AccessRightsMatcher updateAccessFor(final Class dtoClass, final Role role) { - return new AccessRightsMatcher(dtoClass, role, AccessFor::update); - } - - protected AccessRightsMatcher readAccessFor(final Class dtoClass, final Role role) { - return new AccessRightsMatcher(dtoClass, role, AccessFor::read); - } - - // This class should have the same generics as the outer class, but then the - // method references (AccessFor::*) can't be resolved anymore by the Java compiler. - protected static class AccessRightsMatcher { - - private final Object dtoClass; - private final Role role; - - private final String[] namesOfFieldsWithAccessForAnnotation; - private final String[] namesOfAccessibleFields; - - AccessRightsMatcher(final Class dtoClass, final Role role, final Function[]> access) { - this.dtoClass = dtoClass; - this.role = role; - - final Set fieldsWithAccessForAnnotation = determineFieldsWithAccessForAnnotation(dtoClass); - this.namesOfFieldsWithAccessForAnnotation = fieldsWithAccessForAnnotation.stream() - .map(Field::getName) - .collect(Collectors.toList()) - .toArray(new String[] {}); - this.namesOfAccessibleFields = fieldsWithAccessForAnnotation.stream() - .filter(f -> allows(f, access, role)) - .map(Field::getName) - .collect(Collectors.toList()) - .toArray(new String[] {}); - } - - public void shouldBeExactlyFor(final String... expectedFields) { - assertThat(namesOfAccessibleFields).containsExactlyInAnyOrder(expectedFields); - } - - public void shouldBeForNothing() { - assertThat(namesOfAccessibleFields).isEmpty(); - } - - public void shouldBeForAllFields() { - assertThat(namesOfAccessibleFields).containsExactlyInAnyOrder(namesOfFieldsWithAccessForAnnotation); - } - - private static Set determineFieldsWithAccessForAnnotation(final Class dtoClass) { - - final Set fieldsWithAccessForAnnotation = new HashSet<>(); - - for (Field field : dtoClass.getDeclaredFields()) { - if (field.isAnnotationPresent(AccessFor.class)) { - final AccessFor accessFor = field.getAnnotation(AccessFor.class); - fieldsWithAccessForAnnotation.add(field); - } - } - - return fieldsWithAccessForAnnotation; - } - - private static boolean allows( - final Field field, - final Function[]> access, - final Role role) { - if (field.isAnnotationPresent(AccessFor.class)) { - final AccessFor accessFor = field.getAnnotation(AccessFor.class); - Class[] roleClasses = access.apply(accessFor); - return role.coversAny(roleClasses); - } - return false; - } - } - - private String dtoToString(final D dto) { - final StringBuilder fieldValues = new StringBuilder(); - boolean firstField = true; - for (Field field : dto.getClass().getDeclaredFields()) { - if (field.isAnnotationPresent(AccessFor.class)) { - firstField = appendCommaOptionally(fieldValues, firstField); - appendFieldName(fieldValues, field); - appendFieldValue(dto, fieldValues, field); - } - } - return dto.getClass().getSimpleName() + "{" + fieldValues + "}"; - } - - private void appendFieldValue(final D dto, final StringBuilder fieldValues, final Field field) { - final Object value = ReflectionUtil.getValue(dto, field); - final boolean inQuotes = isJHipsterToStringUsingQuotes(field); - if (inQuotes) { - fieldValues.append("'"); - } - fieldValues.append(value); - if (inQuotes) { - fieldValues.append("'"); - } - } - - private void appendFieldName(final StringBuilder fieldValues, final Field field) { - fieldValues.append(removeEnd(field.getName(), "Id")); - fieldValues.append("="); - } - - private boolean appendCommaOptionally(final StringBuilder fieldValues, boolean firstField) { - if (firstField) { - firstField = false; - } else { - fieldValues.append(", "); - } - return firstField; - } - - private boolean isJHipsterToStringUsingQuotes(final Field field) { - return !Number.class.isAssignableFrom(field.getType()) && !Boolean.class.isAssignableFrom(field.getType()); - } - - private void shouldImplementJsonComponent(final Class expectedSuperclass) { - for (Class declaredClass : dtoClass.getDeclaredClasses()) { - if (expectedSuperclass.isAssignableFrom(declaredClass)) { - assertThat(declaredClass.isAnnotationPresent(JsonComponent.class)) - .as(declaredClass + " requires @" + JsonComponent.class.getSimpleName()) - .isTrue(); - assertThat(ReflectionUtil.determineGenericClassParameter(declaredClass, expectedSuperclass, 0)) - .as(declaredClass + " must resolve generic parameter of " + expectedSuperclass + " to type of DTO") - .isEqualTo(dtoClass); - assertThat(Modifier.isPublic(declaredClass.getModifiers())).as(declaredClass + " must be public").isTrue(); - assertThat(Modifier.isStatic(declaredClass.getModifiers())).as(declaredClass + " must be static").isTrue(); - assertThat(Modifier.isFinal(declaredClass.getModifiers())).as(declaredClass + " must not be final").isFalse(); - assertThat(Modifier.isAbstract(declaredClass.getModifiers())).as(declaredClass + " must not be abstract") - .isFalse(); - return; - } - } - fail("no " + expectedSuperclass + " defined for " + dtoClass); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/AssetDTOIntTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/AssetDTOIntTest.java deleted file mode 100644 index 1f8d6121..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/AssetDTOIntTest.java +++ /dev/null @@ -1,229 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.junit.Assert.assertEquals; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; -import org.hostsharing.hsadminng.repository.AssetRepository; -import org.hostsharing.hsadminng.repository.CustomerRepository; -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.AssetService; -import org.hostsharing.hsadminng.service.AssetValidator; -import org.hostsharing.hsadminng.service.MembershipValidator; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.JSonBuilder; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerFinancialContact; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.mapper.AssetMapper; -import org.hostsharing.hsadminng.service.mapper.AssetMapperImpl; -import org.hostsharing.hsadminng.service.mapper.CustomerMapperImpl; -import org.hostsharing.hsadminng.service.mapper.MembershipMapperImpl; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.apache.commons.lang3.RandomUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit4.SpringRunner; - -import java.io.IOException; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.Optional; - -import javax.persistence.EntityManager; - -@JsonTest -@SpringBootTest( - classes = { - CustomerMapperImpl.class, - MembershipMapperImpl.class, - AssetMapperImpl.class, - AssetDTO.JsonSerializer.class, - AssetDTO.JsonDeserializer.class - }) -@RunWith(SpringRunner.class) -public class AssetDTOIntTest { - - private static final Long SOME_CUSTOMER_ID = RandomUtils.nextLong(100, 199); - private static final Integer SOME_CUSTOMER_REFERENCE = 10001; - private static final String SOME_CUSTOMER_PREFIX = "abc"; - private static final String SOME_CUSTOMER_NAME = "Some Customer Name"; - private static final Customer SOME_CUSTOMER = new Customer().id(SOME_CUSTOMER_ID) - .reference(SOME_CUSTOMER_REFERENCE) - .prefix(SOME_CUSTOMER_PREFIX) - .name(SOME_CUSTOMER_NAME); - - private static final Long SOME_MEMBERSHIP_ID = RandomUtils.nextLong(200, 299); - private static final LocalDate SOME_MEMBER_FROM_DATE = LocalDate.parse("2000-12-06"); - private static final Membership SOME_MEMBERSHIP = new Membership().id(SOME_MEMBERSHIP_ID) - .customer(SOME_CUSTOMER) - .memberFromDate(SOME_MEMBER_FROM_DATE); - private static final String SOME_MEMBERSHIP_DISPLAY_LABEL = "Some Customer Name [10001:abc] 2000-12-06 - ..."; - - private static final Long SOME_ASSET_ID = RandomUtils.nextLong(300, 399); - private static final Asset SOME_ASSET = new Asset().id(SOME_ASSET_ID).membership(SOME_MEMBERSHIP); - - @Rule - public MockitoRule mockito = MockitoJUnit.rule(); - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private AssetMapper assetMapper; - - @MockBean - private AssetRepository assetRepository; - - @MockBean - private AssetValidator assetValidator; - - @MockBean - private CustomerRepository customerRepository; - - @MockBean - private MembershipRepository membershipRepository; - - @MockBean - private MembershipValidator membershipValidator; - - @MockBean - private AssetService assetService; - - @MockBean - private EntityManager em; - - @MockBean - private UserRoleAssignmentService userRoleAssignmentService; - - private SecurityContextMock securityContext; - - @Before - public void init() { - given(customerRepository.findById(SOME_CUSTOMER_ID)).willReturn(Optional.of(SOME_CUSTOMER)); - given(membershipRepository.findById(SOME_MEMBERSHIP_ID)).willReturn(Optional.of(SOME_MEMBERSHIP)); - given(assetRepository.findById(SOME_ASSET_ID)).willReturn((Optional.of(SOME_ASSET))); - securityContext = SecurityContextMock.usingMock(userRoleAssignmentService); - } - - @Test - public void shouldSerializePartiallyForFinancialCustomerContact() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser() - .withRole(CustomerDTO.class, SOME_CUSTOMER_ID, Role.of(CustomerFinancialContact.class)); - - final AssetDTO given = createSomeAssetDTO(SOME_ASSET_ID); - - // when - final String actual = objectMapper.writeValueAsString(given); - - // then - given.setRemark(null); - assertEquals(createExpectedJSon(given), actual); - } - - @Test - public void shouldSerializeCompletelyForSupporter() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.SUPPORTER); - final AssetDTO given = createSomeAssetDTO(SOME_ASSET_ID); - - // when - final String actual = objectMapper.writeValueAsString(given); - - // then - assertEquals(createExpectedJSon(given), actual); - } - - @Test - public void shouldNotDeserializeForContractualCustomerContact() { - // given - securityContext.havingAuthenticatedUser() - .withRole(CustomerDTO.class, SOME_CUSTOMER_ID, Role.of(CustomerContractualContact.class)); - final String json = new JSonBuilder() - .withFieldValue("id", SOME_ASSET_ID) - .withFieldValue("remark", "Updated Remark") - .toString(); - - // when - final Throwable actual = catchThrowable(() -> objectMapper.readValue(json, AssetDTO.class)); - - // then - assertThat(actual).isInstanceOfSatisfying( - BadRequestAlertException.class, - bre -> assertThat(bre.getMessage()) - .isEqualTo( - "Update of field AssetDTO.remark prohibited for current user role(s): CustomerContractualContact")); - } - - @Test - public void shouldDeserializeForAdminIfRemarkIsChanged() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN); - final String json = new JSonBuilder() - .withFieldValue("id", SOME_ASSET_ID) - .withFieldValue("remark", "Updated Remark") - .toString(); - - // when - final AssetDTO actual = objectMapper.readValue(json, AssetDTO.class); - - // then - final AssetDTO expected = new AssetDTO(); - expected.setId(SOME_ASSET_ID); - expected.setMembershipId(SOME_MEMBERSHIP_ID); - expected.setRemark("Updated Remark"); - expected.setMembershipDisplayLabel(SOME_MEMBERSHIP_DISPLAY_LABEL); - assertThat(actual).isEqualToIgnoringGivenFields(expected, "displayLabel"); - } - - // --- only test fixture below --- - - private String createExpectedJSon(AssetDTO dto) { - return new JSonBuilder() - .withFieldValueIfPresent("id", dto.getId()) - .withFieldValueIfPresent("documentDate", dto.getDocumentDate().toString()) - .withFieldValueIfPresent("valueDate", dto.getValueDate().toString()) - .withFieldValueIfPresent("action", dto.getAction().name()) - .withFieldValueIfPresent("amount", dto.getAmount().doubleValue()) - .withFieldValueIfPresent("remark", dto.getRemark()) - .withFieldValueIfPresent("membershipId", dto.getMembershipId()) - .withFieldValue("membershipDisplayLabel", dto.getMembershipDisplayLabel()) - .toString(); - } - - private AssetDTO createSomeAssetDTO(final long id) { - final AssetDTO given = new AssetDTO(); - given.setId(id); - given.setAction(AssetAction.PAYMENT); - given.setAmount(new BigDecimal("512.01")); - given.setDocumentDate(LocalDate.parse("2019-04-27")); - given.setValueDate(LocalDate.parse("2019-04-28")); - given.setMembershipId(SOME_MEMBERSHIP_ID); - given.setRemark("Some Remark"); - given.setMembershipDisplayLabel("Display Label for Membership #" + SOME_MEMBERSHIP_ID); - return given; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/AssetDTOUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/AssetDTOUnitTest.java deleted file mode 100644 index 3f71acad..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/AssetDTOUnitTest.java +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.Admin; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.util.RandomUtil; - -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.RandomUtils; -import org.junit.Test; - -import java.math.BigDecimal; -import java.time.LocalDate; - -public class AssetDTOUnitTest extends AccessMappingsUnitTestBase { - - public AssetDTOUnitTest() { - super(AssetDTO.class, AssetDTOUnitTest::createSampleDTO, AssetDTOUnitTest::createRandomDTO); - } - - @Test - public void shouldHaveProperAccessForAdmin() { - initAccessFor(AssetDTO.class, Admin.ROLE).shouldBeExactlyFor( - "membershipId", - "documentDate", - "amount", - "action", - "valueDate", - "remark"); - updateAccessFor(AssetDTO.class, Admin.ROLE).shouldBeExactlyFor("remark"); - readAccessFor(AssetDTO.class, Admin.ROLE).shouldBeForAllFields(); - } - - @Test - public void shouldHaveProperAccessForContractualContact() { - initAccessFor(AssetDTO.class, CustomerContractualContact.ROLE).shouldBeForNothing(); - updateAccessFor(AssetDTO.class, CustomerContractualContact.ROLE).shouldBeForNothing(); - readAccessFor(AssetDTO.class, CustomerContractualContact.ROLE).shouldBeExactlyFor( - "id", - "membershipId", - "documentDate", - "amount", - "action", - "valueDate", - "membershipDisplayLabel"); - } - - @Test - public void shouldHaveNoAccessForTechnicalContact() { - initAccessFor(AssetDTO.class, Role.CustomerTechnicalContact.ROLE).shouldBeForNothing(); - updateAccessFor(AssetDTO.class, Role.CustomerTechnicalContact.ROLE).shouldBeForNothing(); - readAccessFor(AssetDTO.class, Role.CustomerTechnicalContact.ROLE).shouldBeForNothing(); - } - - @Test - public void shouldHaveNoAccessForNormalUsersWithinCustomerRealm() { - initAccessFor(AssetDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - updateAccessFor(AssetDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - readAccessFor(AssetDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - } - - // --- only test fixture below --- - - private static AssetDTO createSampleDTO(final Long id, final Long parentId) { - final AssetDTO dto = new AssetDTO(); - dto.setId(id); - dto.setDocumentDate(LocalDate.parse("2000-12-07")); - dto.setAmount(new BigDecimal("512.01")); - dto.setAction(AssetAction.PAYMENT); - dto.setRemark("Some Remark"); - dto.setValueDate(LocalDate.parse("2000-12-18")); - dto.setMembershipId(parentId); - dto.setMembershipDisplayLabel("Some Membership"); - return dto; - } - - private static AssetDTO createRandomDTO(final Long id, final Long parentId) { - final AssetDTO dto = new AssetDTO(); - dto.setId(id); - final LocalDate randomDate = LocalDate.parse("2000-12-07").plusDays(RandomUtils.nextInt(1, 999)); - dto.setDocumentDate(randomDate); - dto.setAmount(new BigDecimal(RandomUtils.nextDouble())); - dto.setAction(RandomUtil.generateEnumValue(AssetAction.class)); - dto.setRemark(RandomStringUtils.randomAlphanumeric(20)); - dto.setValueDate(randomDate.plusDays(RandomUtils.nextInt(1, 99))); - dto.setMembershipId(parentId); - dto.setMembershipDisplayLabel("The Membership #" + dto.getMembershipId()); - return dto; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/CustomerDTOUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/CustomerDTOUnitTest.java deleted file mode 100644 index b9214af6..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/CustomerDTOUnitTest.java +++ /dev/null @@ -1,188 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.enumeration.CustomerKind; -import org.hostsharing.hsadminng.domain.enumeration.VatRegion; -import org.hostsharing.hsadminng.repository.CustomerRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.CustomerService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.JSonBuilder; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerTechnicalContact; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.mapper.CustomerMapper; -import org.hostsharing.hsadminng.service.mapper.CustomerMapperImpl; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit4.SpringRunner; - -import java.io.IOException; -import java.util.Optional; - -@JsonTest -@SpringBootTest( - classes = { - CustomerMapperImpl.class, - CustomerRepository.class, - CustomerService.class, - CustomerDTO.CustomerJsonSerializer.class, - CustomerDTO.CustomerJsonDeserializer.class }) -@RunWith(SpringRunner.class) -public class CustomerDTOUnitTest { - - @Rule - public MockitoRule mockito = MockitoJUnit.rule(); - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private CustomerMapper customerMapper; - - @MockBean - private CustomerRepository customerRepository; - - @MockBean - private CustomerService customerService; - - @MockBean - private UserRoleAssignmentService userRoleAssignmentService; - - private SecurityContextMock securityContext; - - @Before - public void init() { - securityContext = SecurityContextMock.usingMock(userRoleAssignmentService); - } - - @Test - public void testSerializationAsContractualCustomerContact() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser().withRole(CustomerDTO.class, 1234L, Role.of(CustomerContractualContact.class)); - CustomerDTO given = createSomeCustomerDTO(1234L); - - // when - String actual = objectMapper.writeValueAsString(given); - - // then - given.setRemark(null); - assertEquals(createExpectedJSon(given), actual); - } - - @Test - public void testSerializationAsTechnicalCustomerUser() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser().withRole(CustomerDTO.class, 1234L, Role.of(CustomerTechnicalContact.class)); - CustomerDTO given = createSomeCustomerDTO(1234L); - - // when - String actual = objectMapper.writeValueAsString(given); - - // then - final String expectedJSon = new JSonBuilder() - .withFieldValue("id", given.getId()) - .withFieldValue("reference", given.getReference()) - .withFieldValue("prefix", given.getPrefix()) - .withFieldValue("name", given.getName()) - .withFieldValue("displayLabel", given.getDisplayLabel()) - .toString(); - assertEquals(expectedJSon, actual); - } - - @Test - public void testSerializationAsSupporter() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.SUPPORTER); - CustomerDTO given = createSomeCustomerDTO(1234L); - - // when - String actual = objectMapper.writeValueAsString(given); - - // then - assertThat(actual).isEqualTo(createExpectedJSon(given)); - } - - @Test - public void testDeserializeAsContractualCustomerContact() throws IOException { - // given - securityContext.havingAuthenticatedUser().withRole(CustomerDTO.class, 1234L, Role.of(CustomerContractualContact.class)); - given(customerRepository.findById(1234L)).willReturn(Optional.of(new Customer().id(1234L))); - String json = "{\"id\":1234,\"contractualSalutation\":\"Hallo Updated\",\"billingSalutation\":\"Moin Updated\"}"; - - // when - CustomerDTO actual = objectMapper.readValue(json, CustomerDTO.class); - - // then - CustomerDTO expected = new CustomerDTO(); - expected.setId(1234L); - expected.setContractualSalutation("Hallo Updated"); - expected.setBillingSalutation("Moin Updated"); - assertThat(actual).isEqualToIgnoringGivenFields(expected, "displayLabel"); - } - - // --- only test fixture below --- - - private String createExpectedJSon(CustomerDTO dto) { - return new JSonBuilder() - .withFieldValueIfPresent("id", dto.getId()) - .withFieldValueIfPresent("reference", dto.getReference()) - .withFieldValueIfPresent("prefix", dto.getPrefix()) - .withFieldValueIfPresent("name", dto.getName()) - .withFieldValueIfPresent("kind", "LEGAL") - .toJSonNullFieldDefinition("birthDate") - .toJSonNullFieldDefinition("birthPlace") - .withFieldValueIfPresent("registrationCourt", "Registergericht") - .withFieldValueIfPresent("registrationNumber", "Registernummer") - .withFieldValueIfPresent("vatRegion", "DOMESTIC") - .withFieldValueIfPresent("vatNumber", "DE1234") - .withFieldValueIfPresent("contractualSalutation", dto.getContractualSalutation()) - .withFieldValueIfPresent("contractualAddress", dto.getContractualAddress()) - .withFieldValueIfPresent("billingSalutation", dto.getBillingSalutation()) - .withFieldValueIfPresent("billingAddress", dto.getBillingAddress()) - .withFieldValueIfPresent("remark", dto.getRemark()) - .withFieldValueIfPresent("displayLabel", dto.getDisplayLabel()) - .toString(); - } - - private CustomerDTO createSomeCustomerDTO(final long id) { - final CustomerDTO given = new CustomerDTO(); - given.setId(id); - given.setReference(10001); - given.setPrefix("abc"); - given.setName("Mein Name"); - given.setKind(CustomerKind.LEGAL); - given.setRegistrationCourt("Registergericht"); - given.setRegistrationNumber("Registernummer"); - given.setVatRegion(VatRegion.DOMESTIC); - given.setVatNumber("DE1234"); - given.setContractualAddress("Eine Adresse"); - given.setContractualSalutation("Hallo"); - given.setBillingAddress("Noch eine Adresse"); - given.setBillingSalutation("Moin"); - given.setRemark("Eine Bemerkung"); - given.setDisplayLabel("Display Label"); - return given; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOIntTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOIntTest.java deleted file mode 100644 index 3b8ff9a8..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOIntTest.java +++ /dev/null @@ -1,197 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.hostsharing.hsadminng.service.dto.MembershipDTOUnitTest.createSampleDTO; -import static org.junit.Assert.assertEquals; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.repository.CustomerRepository; -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.MembershipService; -import org.hostsharing.hsadminng.service.MembershipValidator; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.JSonBuilder; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerFinancialContact; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.mapper.CustomerMapperImpl; -import org.hostsharing.hsadminng.service.mapper.MembershipMapper; -import org.hostsharing.hsadminng.service.mapper.MembershipMapperImpl; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.apache.commons.lang3.RandomUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit4.SpringRunner; - -import java.io.IOException; -import java.util.Objects; -import java.util.Optional; - -import javax.persistence.EntityManager; - -@JsonTest -@SpringBootTest( - classes = { - CustomerMapperImpl.class, - MembershipMapperImpl.class, - MembershipMapperImpl.class, - MembershipDTO.JsonSerializer.class, - MembershipDTO.JsonDeserializer.class - }) -@RunWith(SpringRunner.class) -public class MembershipDTOIntTest { - - private static final Long SOME_CUSTOMER_ID = RandomUtils.nextLong(100, 199); - private static final Integer SOME_CUSTOMER_REFERENCE = 10001; - private static final String SOME_CUSTOMER_PREFIX = "abc"; - private static final String SOME_CUSTOMER_NAME = "Some Customer Name"; - private static final String SOME_CUSTOMER_DISPLAY_LABEL = "Some Customer Name [10001:abc]"; - private static final Customer SOME_CUSTOMER = new Customer().id(SOME_CUSTOMER_ID) - .reference(SOME_CUSTOMER_REFERENCE) - .prefix(SOME_CUSTOMER_PREFIX) - .name(SOME_CUSTOMER_NAME); - - private static final Long SOME_SEPA_MANDATE_ID = RandomUtils.nextLong(300, 399); - private static final Membership SOME_SEPA_MANDATE = new Membership().id(SOME_SEPA_MANDATE_ID).customer(SOME_CUSTOMER); - - @Rule - public MockitoRule mockito = MockitoJUnit.rule(); - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private MembershipMapper membershipMapper; - - @MockBean - private CustomerRepository customerRepository; - - @MockBean - private MembershipRepository membershipRepository; - - @MockBean - private MembershipValidator membershipValidator; - - @MockBean - private MembershipService MembershipService; - - @MockBean - private EntityManager em; - - @MockBean - public UserRoleAssignmentService userRoleAssignmentService; - - private SecurityContextMock securityContext; - - @Before - public void init() { - given(customerRepository.findById(SOME_CUSTOMER_ID)).willReturn(Optional.of(SOME_CUSTOMER)); - given(membershipRepository.findById(SOME_SEPA_MANDATE_ID)).willReturn((Optional.of(SOME_SEPA_MANDATE))); - - securityContext = SecurityContextMock.usingMock(userRoleAssignmentService); - } - - @Test - public void shouldSerializePartiallyForFinancialCustomerContact() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser() - .withRole(CustomerDTO.class, SOME_CUSTOMER_ID, CustomerFinancialContact.ROLE); - final MembershipDTO given = createSampleDTO(SOME_SEPA_MANDATE_ID, SOME_CUSTOMER_ID); - - // when - final String actual = objectMapper.writeValueAsString(given); - - // then - given.setRemark(null); - assertEquals(createExpectedJSon(given), actual); - } - - @Test - public void shouldSerializeCompletelyForSupporter() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.SUPPORTER); - final MembershipDTO given = createSampleDTO(SOME_SEPA_MANDATE_ID, SOME_CUSTOMER_ID); - - // when - final String actual = objectMapper.writeValueAsString(given); - - // then - assertEquals(createExpectedJSon(given), actual); - } - - @Test - public void shouldNotDeserializeForContractualCustomerContact() { - // given - securityContext.havingAuthenticatedUser() - .withRole(CustomerDTO.class, SOME_CUSTOMER_ID, CustomerContractualContact.ROLE); - final String json = new JSonBuilder() - .withFieldValue("id", SOME_SEPA_MANDATE_ID) - .withFieldValue("remark", "Updated Remark") - .toString(); - - // when - final Throwable actual = catchThrowable(() -> objectMapper.readValue(json, MembershipDTO.class)); - - // then - assertThat(actual).isInstanceOfSatisfying( - BadRequestAlertException.class, - bre -> assertThat(bre.getMessage()).isEqualTo( - "Update of field MembershipDTO.remark prohibited for current user role(s): CustomerContractualContact")); - } - - @Test - public void shouldDeserializeForAdminIfRemarkIsChanged() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN); - final String json = new JSonBuilder() - .withFieldValue("id", SOME_SEPA_MANDATE_ID) - .withFieldValue("remark", "Updated Remark") - .toString(); - - // when - final MembershipDTO actual = objectMapper.readValue(json, MembershipDTO.class); - - // then - final MembershipDTO expected = new MembershipDTO(); - expected.setId(SOME_SEPA_MANDATE_ID); - expected.setCustomerId(SOME_CUSTOMER_ID); - expected.setRemark("Updated Remark"); - assertThat(actual).isEqualToIgnoringGivenFields(expected, "customerPrefix", "customerDisplayLabel", "displayLabel"); - } - - // --- only test fixture below --- - - private String createExpectedJSon(MembershipDTO dto) { - return new JSonBuilder() - .withFieldValueIfPresent("id", dto.getId()) - .withFieldValueIfPresent("admissionDocumentDate", Objects.toString(dto.getAdmissionDocumentDate())) - .withFieldValueIfPresent("cancellationDocumentDate", Objects.toString(dto.getCancellationDocumentDate())) - .withFieldValueIfPresent("memberFromDate", Objects.toString(dto.getMemberFromDate())) - .withFieldValueIfPresent("memberUntilDate", Objects.toString(dto.getMemberUntilDate())) - .withFieldValueIfPresent("remark", dto.getRemark()) - .withFieldValueIfPresent("customerId", dto.getCustomerId()) - .withFieldValue("customerPrefix", dto.getCustomerPrefix()) - .withFieldValue("customerDisplayLabel", dto.getCustomerDisplayLabel()) - .withFieldValue("displayLabel", dto.getDisplayLabel()) - .toString(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOUnitTest.java deleted file mode 100644 index 79c21466..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOUnitTest.java +++ /dev/null @@ -1,108 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.Admin; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerTechnicalContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.Supporter; - -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.RandomUtils; -import org.junit.Test; - -import java.time.LocalDate; - -public class MembershipDTOUnitTest extends AccessMappingsUnitTestBase { - - public MembershipDTOUnitTest() { - super(MembershipDTO.class, MembershipDTOUnitTest::createSampleDTO, MembershipDTOUnitTest::createRandomDTO); - } - - @Test - public void shouldHaveProperAccessForAdmin() { - initAccessFor(MembershipDTO.class, Admin.ROLE).shouldBeExactlyFor( - "admissionDocumentDate", - "cancellationDocumentDate", - "memberFromDate", - "memberUntilDate", - "customerId", - "remark"); - updateAccessFor(MembershipDTO.class, Admin.ROLE).shouldBeExactlyFor( - "cancellationDocumentDate", - "memberUntilDate", - "remark"); - readAccessFor(MembershipDTO.class, Admin.ROLE).shouldBeForAllFields(); - } - - @Test - public void shouldHaveProperAccessForSupporter() { - initAccessFor(MembershipDTO.class, Supporter.ROLE).shouldBeForNothing(); - updateAccessFor(MembershipDTO.class, Supporter.ROLE).shouldBeForNothing(); - readAccessFor(MembershipDTO.class, Supporter.ROLE).shouldBeForAllFields(); - } - - @Test - public void shouldHaveProperAccessForContractualContact() { - initAccessFor(MembershipDTO.class, CustomerContractualContact.ROLE).shouldBeForNothing(); - updateAccessFor(MembershipDTO.class, CustomerContractualContact.ROLE).shouldBeForNothing(); - readAccessFor(MembershipDTO.class, CustomerContractualContact.ROLE).shouldBeExactlyFor( - "id", - "admissionDocumentDate", - "cancellationDocumentDate", - "memberFromDate", - "memberUntilDate", - "customerId", - "customerPrefix", - "customerDisplayLabel", - "displayLabel"); - } - - @Test - public void shouldHaveNoAccessForTechnicalContact() { - initAccessFor(MembershipDTO.class, CustomerTechnicalContact.ROLE).shouldBeForNothing(); - updateAccessFor(MembershipDTO.class, CustomerTechnicalContact.ROLE).shouldBeForNothing(); - readAccessFor(MembershipDTO.class, CustomerTechnicalContact.ROLE).shouldBeForNothing(); - } - - @Test - public void shouldHaveNoAccessForNormalUsersWithinCustomerRealm() { - initAccessFor(MembershipDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - updateAccessFor(MembershipDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - readAccessFor(MembershipDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - } - - // --- only test fixture below --- - - static MembershipDTO createSampleDTO(final Long id, final Long parentId) { - final MembershipDTO dto = new MembershipDTO(); - dto.setId(id); - final LocalDate referenceDate = LocalDate.parse("2000-12-07"); - dto.setAdmissionDocumentDate(referenceDate); - dto.setCancellationDocumentDate(referenceDate.plusDays(3500)); - dto.setMemberFromDate(referenceDate.plusDays(4)); - dto.setMemberUntilDate(referenceDate.plusDays(3500).plusDays(400).withDayOfYear(1).minusDays(1)); - dto.setRemark("Some Remark"); - dto.setCustomerId(parentId); - dto.setCustomerPrefix("abc"); - dto.setCustomerDisplayLabel("ABC GmbH [abc:10001]"); - dto.setDisplayLabel("ABC GmbH [abc:10001] 2000-12-11 - 2011-12-31"); - return dto; - } - - public static MembershipDTO createRandomDTO(final Long id, final Long parentId) { - final MembershipDTO dto = new MembershipDTO(); - dto.setId(id); - final LocalDate randomDate = LocalDate.parse("2000-12-07").plusDays(RandomUtils.nextInt(1, 999)); - dto.setAdmissionDocumentDate(randomDate); - dto.setCancellationDocumentDate(randomDate.plusDays(3500)); - dto.setMemberFromDate(randomDate.plusDays(4)); - dto.setMemberUntilDate(randomDate.plusDays(3500).plusDays(400).withDayOfYear(1).minusDays(1)); - dto.setRemark(RandomStringUtils.randomAlphanumeric(20).toUpperCase()); - dto.setCustomerId(parentId); - dto.setCustomerPrefix(RandomStringUtils.randomAlphabetic(3).toLowerCase()); - dto.setCustomerDisplayLabel(RandomStringUtils.randomAlphabetic(13)); - dto.setDisplayLabel(dto.getCustomerDisplayLabel() + dto.getMemberFromDate() + " - " + dto.getMemberUntilDate()); - return dto; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTOIntTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTOIntTest.java deleted file mode 100644 index 34807ba1..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTOIntTest.java +++ /dev/null @@ -1,206 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.hostsharing.hsadminng.service.dto.SepaMandateDTOUnitTest.createSampleDTO; -import static org.junit.Assert.assertEquals; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.SepaMandate; -import org.hostsharing.hsadminng.repository.CustomerRepository; -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.repository.SepaMandateRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.MembershipValidator; -import org.hostsharing.hsadminng.service.SepaMandateService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.JSonBuilder; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerFinancialContact; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.mapper.CustomerMapperImpl; -import org.hostsharing.hsadminng.service.mapper.MembershipMapperImpl; -import org.hostsharing.hsadminng.service.mapper.SepaMandateMapper; -import org.hostsharing.hsadminng.service.mapper.SepaMandateMapperImpl; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.apache.commons.lang3.RandomUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit4.SpringRunner; - -import java.io.IOException; -import java.util.Objects; -import java.util.Optional; - -import javax.persistence.EntityManager; - -@JsonTest -@SpringBootTest( - classes = { - CustomerMapperImpl.class, - MembershipMapperImpl.class, - SepaMandateMapperImpl.class, - SepaMandateDTO.JsonSerializer.class, - SepaMandateDTO.JsonDeserializer.class - }) -@RunWith(SpringRunner.class) -public class SepaMandateDTOIntTest { - - private static final Long SOME_CUSTOMER_ID = RandomUtils.nextLong(100, 199); - private static final Integer SOME_CUSTOMER_REFERENCE = 10001; - private static final String SOME_CUSTOMER_PREFIX = "abc"; - private static final String SOME_CUSTOMER_NAME = "Some Customer Name"; - private static final String SOME_CUSTOMER_DISPLAY_LABEL = "Some Customer Name [10001:abc]"; - private static final Customer SOME_CUSTOMER = new Customer().id(SOME_CUSTOMER_ID) - .reference(SOME_CUSTOMER_REFERENCE) - .prefix(SOME_CUSTOMER_PREFIX) - .name(SOME_CUSTOMER_NAME); - - private static final Long SOME_SEPA_MANDATE_ID = RandomUtils.nextLong(300, 399); - private static final SepaMandate SOME_SEPA_MANDATE = new SepaMandate().id(SOME_SEPA_MANDATE_ID).customer(SOME_CUSTOMER); - - @Rule - public MockitoRule mockito = MockitoJUnit.rule(); - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private SepaMandateMapper sepaMandateMapper; - - @MockBean - private SepaMandateRepository sepaMandateRepository; - - @MockBean - private CustomerRepository customerRepository; - - @MockBean - private MembershipRepository membershipRepository; - - @MockBean - private MembershipValidator membershipValidator; - - @MockBean - private SepaMandateService sepaMandateService; - - @MockBean - private EntityManager em; - - @MockBean - public UserRoleAssignmentService userRoleAssignmentService; - - private SecurityContextMock securityContext; - - @Before - public void init() { - given(customerRepository.findById(SOME_CUSTOMER_ID)).willReturn(Optional.of(SOME_CUSTOMER)); - given(sepaMandateRepository.findById(SOME_SEPA_MANDATE_ID)).willReturn((Optional.of(SOME_SEPA_MANDATE))); - - securityContext = SecurityContextMock.usingMock(userRoleAssignmentService); - } - - @Test - public void shouldSerializePartiallyForFinancialCustomerContact() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser() - .withRole(CustomerDTO.class, SOME_CUSTOMER_ID, Role.of(CustomerFinancialContact.class)); - final SepaMandateDTO given = createSampleDTO(SOME_SEPA_MANDATE_ID, SOME_CUSTOMER_ID); - - // when - final String actual = objectMapper.writeValueAsString(given); - - // then - given.setRemark(null); - assertEquals(createExpectedJSon(given), actual); - } - - @Test - public void shouldSerializeCompletelyForSupporter() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.SUPPORTER); - final SepaMandateDTO given = createSampleDTO(SOME_SEPA_MANDATE_ID, SOME_CUSTOMER_ID); - - // when - final String actual = objectMapper.writeValueAsString(given); - - // then - assertEquals(createExpectedJSon(given), actual); - } - - @Test - public void shouldNotDeserializeForContractualCustomerContact() { - // given - securityContext.havingAuthenticatedUser() - .withRole(CustomerDTO.class, SOME_CUSTOMER_ID, CustomerContractualContact.ROLE); - final String json = new JSonBuilder() - .withFieldValue("id", SOME_SEPA_MANDATE_ID) - .withFieldValue("remark", "Updated Remark") - .toString(); - - // when - final Throwable actual = catchThrowable(() -> objectMapper.readValue(json, SepaMandateDTO.class)); - - // then - assertThat(actual).isInstanceOfSatisfying( - BadRequestAlertException.class, - bre -> assertThat(bre.getMessage()).isEqualTo( - "Update of field SepaMandateDTO.remark prohibited for current user role(s): CustomerContractualContact")); - } - - @Test - public void shouldDeserializeForAdminIfRemarkIsChanged() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN); - final String json = new JSonBuilder() - .withFieldValue("id", SOME_SEPA_MANDATE_ID) - .withFieldValue("remark", "Updated Remark") - .toString(); - - // when - final SepaMandateDTO actual = objectMapper.readValue(json, SepaMandateDTO.class); - - // then - final SepaMandateDTO expected = new SepaMandateDTO(); - expected.setId(SOME_SEPA_MANDATE_ID); - expected.setCustomerId(SOME_CUSTOMER_ID); - expected.setRemark("Updated Remark"); - expected.setCustomerDisplayLabel(SOME_CUSTOMER_DISPLAY_LABEL); - assertThat(actual).isEqualToIgnoringGivenFields(expected, "displayLabel"); - } - - // --- only test fixture below --- - - private String createExpectedJSon(SepaMandateDTO dto) { - return new JSonBuilder() - .withFieldValueIfPresent("id", dto.getId()) - .withFieldValueIfPresent("reference", dto.getReference()) - .withFieldValueIfPresent("iban", dto.getIban()) - .withFieldValueIfPresent("bic", dto.getBic()) - .withFieldValueIfPresent("grantingDocumentDate", Objects.toString(dto.getGrantingDocumentDate())) - .withFieldValueIfPresent("revokationDocumentDate", Objects.toString(dto.getRevokationDocumentDate())) - .withFieldValueIfPresent("validFromDate", Objects.toString(dto.getValidFromDate())) - .withFieldValueIfPresent("validUntilDate", Objects.toString(dto.getValidUntilDate())) - .withFieldValueIfPresent("lastUsedDate", Objects.toString(dto.getLastUsedDate())) - .withFieldValueIfPresent("remark", dto.getRemark()) - .withFieldValueIfPresent("customerId", dto.getCustomerId()) - .withFieldValue("customerDisplayLabel", dto.getCustomerDisplayLabel()) - .toString(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTOUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTOUnitTest.java deleted file mode 100644 index 76bd52bd..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/SepaMandateDTOUnitTest.java +++ /dev/null @@ -1,137 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.Admin; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerTechnicalContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.Supporter; - -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.RandomUtils; -import org.junit.Test; - -import java.time.LocalDate; - -public class SepaMandateDTOUnitTest extends AccessMappingsUnitTestBase { - - public SepaMandateDTOUnitTest() { - super(SepaMandateDTO.class, SepaMandateDTOUnitTest::createSampleDTO, SepaMandateDTOUnitTest::createRandomDTO); - } - - @Test - public void shouldHaveProperAccessForAdmin() { - initAccessFor(SepaMandateDTO.class, Role.of(Admin.class)).shouldBeExactlyFor( - "grantingDocumentDate", - "bic", - "remark", - "validUntilDate", - "customerId", - "validFromDate", - "iban", - "revokationDocumentDate", - "lastUsedDate", - "reference"); - updateAccessFor(SepaMandateDTO.class, Role.of(Admin.class)).shouldBeExactlyFor( - "remark", - "validUntilDate", - "revokationDocumentDate", - "lastUsedDate"); - readAccessFor(SepaMandateDTO.class, Role.of(Admin.class)).shouldBeForAllFields(); - } - - @Test - public void shouldHaveProperAccessForSupporter() { - initAccessFor(SepaMandateDTO.class, Role.of(Supporter.class)).shouldBeExactlyFor( - "grantingDocumentDate", - "bic", - "validUntilDate", - "customerId", - "validFromDate", - "iban", - "reference"); - updateAccessFor(SepaMandateDTO.class, Role.of(Supporter.class)).shouldBeExactlyFor( - "remark", - "validUntilDate", - "revokationDocumentDate"); - readAccessFor(SepaMandateDTO.class, Role.of(Supporter.class)).shouldBeForAllFields(); - } - - @Test - public void shouldHaveProperAccessForContractualContact() { - initAccessFor(SepaMandateDTO.class, Role.of(CustomerContractualContact.class)).shouldBeExactlyFor( - "grantingDocumentDate", - "bic", - "validUntilDate", - "customerId", - "validFromDate", - "iban", - "reference"); - updateAccessFor(SepaMandateDTO.class, Role.of(CustomerContractualContact.class)).shouldBeExactlyFor( - "validUntilDate", - "revokationDocumentDate"); - readAccessFor(SepaMandateDTO.class, Role.of(CustomerContractualContact.class)).shouldBeExactlyFor( - "grantingDocumentDate", - "bic", - "id", - "validUntilDate", - "customerId", - "validFromDate", - "iban", - "revokationDocumentDate", - "customerDisplayLabel", - "lastUsedDate", - "reference"); - } - - @Test - public void shouldHaveNoAccessForTechnicalContact() { - initAccessFor(SepaMandateDTO.class, Role.of(CustomerTechnicalContact.class)).shouldBeForNothing(); - updateAccessFor(SepaMandateDTO.class, Role.of(CustomerTechnicalContact.class)).shouldBeForNothing(); - readAccessFor(SepaMandateDTO.class, Role.of(CustomerTechnicalContact.class)).shouldBeForNothing(); - } - - @Test - public void shouldHaveNoAccessForNormalUsersWithinCustomerRealm() { - initAccessFor(SepaMandateDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - updateAccessFor(SepaMandateDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - readAccessFor(SepaMandateDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - } - - // --- only test fixture below --- - - public static SepaMandateDTO createSampleDTO(final Long id, final Long parentId) { - final SepaMandateDTO dto = new SepaMandateDTO(); - dto.setId(id); - dto.setReference("Some Reference"); - dto.setGrantingDocumentDate(LocalDate.parse("2000-12-07")); - dto.setRevokationDocumentDate(LocalDate.parse("2019-04-27")); - dto.setValidFromDate(LocalDate.parse("2000-12-18")); - dto.setValidUntilDate(LocalDate.parse("2019-05-31")); - dto.setLastUsedDate(LocalDate.parse("2019-04-04")); - dto.setIban("DE1234IBAN"); - dto.setBic("BIC1234"); - dto.setRemark("Some Remark"); - dto.setCustomerId(parentId); - dto.setCustomerDisplayLabel("abc"); - return dto; - } - - public static SepaMandateDTO createRandomDTO(final Long id, final Long parentId) { - final SepaMandateDTO dto = new SepaMandateDTO(); - dto.setId(id); - dto.setReference(RandomStringUtils.randomAlphanumeric(10)); - final LocalDate randomDate = LocalDate.parse("2000-12-07").plusDays(RandomUtils.nextInt(1, 999)); - dto.setGrantingDocumentDate(randomDate); - dto.setRevokationDocumentDate(randomDate.plusDays(RandomUtils.nextInt(1100, 2999))); - dto.setValidFromDate(randomDate.plusDays(RandomUtils.nextInt(0, 7))); - dto.setValidUntilDate(dto.getRevokationDocumentDate().plusDays(7)); - dto.setLastUsedDate(dto.getRevokationDocumentDate().minusDays(20)); - dto.setIban(RandomStringUtils.randomAlphanumeric(20).toUpperCase()); - dto.setBic(RandomStringUtils.randomAlphanumeric(10).toUpperCase()); - dto.setRemark(RandomStringUtils.randomAlphanumeric(20).toUpperCase()); - dto.setCustomerId(parentId); - dto.setCustomerDisplayLabel(RandomStringUtils.randomAlphabetic(3).toLowerCase()); - return dto; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOIntTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOIntTest.java deleted file mode 100644 index 0948d163..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOIntTest.java +++ /dev/null @@ -1,227 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.junit.Assert.assertEquals; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; -import org.hostsharing.hsadminng.repository.CustomerRepository; -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.repository.ShareRepository; -import org.hostsharing.hsadminng.service.MembershipValidator; -import org.hostsharing.hsadminng.service.ShareService; -import org.hostsharing.hsadminng.service.ShareValidator; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.JSonBuilder; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerFinancialContact; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.mapper.CustomerMapperImpl; -import org.hostsharing.hsadminng.service.mapper.MembershipMapperImpl; -import org.hostsharing.hsadminng.service.mapper.ShareMapper; -import org.hostsharing.hsadminng.service.mapper.ShareMapperImpl; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.apache.commons.lang3.RandomUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit4.SpringRunner; - -import java.io.IOException; -import java.time.LocalDate; -import java.util.Optional; - -import javax.persistence.EntityManager; - -@JsonTest -@SpringBootTest( - classes = { - CustomerMapperImpl.class, - MembershipMapperImpl.class, - ShareMapperImpl.class, - ShareDTO.JsonSerializer.class, - ShareDTO.JsonDeserializer.class - }) -@RunWith(SpringRunner.class) -public class ShareDTOIntTest { - - private static final Long SOME_CUSTOMER_ID = RandomUtils.nextLong(100, 199); - private static final Integer SOME_CUSTOMER_REFERENCE = 10001; - private static final String SOME_CUSTOMER_PREFIX = "abc"; - private static final String SOME_CUSTOMER_NAME = "Some Customer Name"; - private static final Customer SOME_CUSTOMER = new Customer().id(SOME_CUSTOMER_ID) - .reference(SOME_CUSTOMER_REFERENCE) - .prefix(SOME_CUSTOMER_PREFIX) - .name(SOME_CUSTOMER_NAME); - - private static final Long SOME_MEMBERSHIP_ID = RandomUtils.nextLong(200, 299); - private static final LocalDate SOME_MEMBER_FROM_DATE = LocalDate.parse("2000-12-06"); - private static final Membership SOME_MEMBERSHIP = new Membership().id(SOME_MEMBERSHIP_ID) - .customer(SOME_CUSTOMER) - .memberFromDate(SOME_MEMBER_FROM_DATE); - private static final String SOME_MEMBERSHIP_DISPLAY_LABEL = "Some Customer Name [10001:abc] 2000-12-06 - ..."; - - private static final Long SOME_SHARE_ID = RandomUtils.nextLong(300, 399); - private static final Share SOME_SHARE = new Share().id(SOME_SHARE_ID).membership(SOME_MEMBERSHIP); - - @Rule - public MockitoRule mockito = MockitoJUnit.rule(); - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private ShareMapper shareMapper; - - @MockBean - private ShareRepository shareRepository; - - @MockBean - private ShareValidator shareValidator; - - @MockBean - private CustomerRepository customerRepository; - - @MockBean - private MembershipRepository membershipRepository; - - @MockBean - private MembershipValidator membershipValidator; - - @MockBean - private ShareService shareService; - - @MockBean - private EntityManager em; - - @MockBean - private UserRoleAssignmentService userRoleAssignmentService; - - private SecurityContextMock securityContext; - - @Before - public void init() { - given(customerRepository.findById(SOME_CUSTOMER_ID)).willReturn(Optional.of(SOME_CUSTOMER)); - given(membershipRepository.findById(SOME_MEMBERSHIP_ID)).willReturn(Optional.of(SOME_MEMBERSHIP)); - given(shareRepository.findById(SOME_SHARE_ID)).willReturn((Optional.of(SOME_SHARE))); - - securityContext = SecurityContextMock.usingMock(userRoleAssignmentService); - } - - @Test - public void shouldSerializePartiallyForFinancialCustomerContact() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser() - .withRole(CustomerDTO.class, SOME_CUSTOMER_ID, CustomerFinancialContact.ROLE); - final ShareDTO given = createSomeShareDTO(SOME_SHARE_ID); - - // when - final String actual = objectMapper.writeValueAsString(given); - - // then - given.setRemark(null); - assertEquals(createExpectedJSon(given), actual); - } - - @Test - public void shouldSerializeCompletelyForSupporter() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser().withAuthority(Role.Supporter.ROLE.authority()); - final ShareDTO given = createSomeShareDTO(SOME_SHARE_ID); - - // when - final String actual = objectMapper.writeValueAsString(given); - - // then - assertEquals(createExpectedJSon(given), actual); - } - - @Test - public void shouldNotDeserializeForContractualCustomerContact() { - // given - securityContext.havingAuthenticatedUser() - .withRole(CustomerDTO.class, SOME_CUSTOMER_ID, CustomerContractualContact.ROLE); - final String json = new JSonBuilder() - .withFieldValue("id", SOME_SHARE_ID) - .withFieldValue("remark", "Updated Remark") - .toString(); - - // when - final Throwable actual = catchThrowable(() -> objectMapper.readValue(json, ShareDTO.class)); - - // then - assertThat(actual).isInstanceOfSatisfying( - BadRequestAlertException.class, - bre -> assertThat(bre.getMessage()) - .isEqualTo( - "Update of field ShareDTO.remark prohibited for current user role(s): CustomerContractualContact")); - } - - @Test - public void shouldDeserializeForAdminIfRemarkIsChanged() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(Role.Admin.ROLE.authority()); - final String json = new JSonBuilder() - .withFieldValue("id", SOME_SHARE_ID) - .withFieldValue("remark", "Updated Remark") - .toString(); - - // when - final ShareDTO actual = objectMapper.readValue(json, ShareDTO.class); - - // then - final ShareDTO expected = new ShareDTO(); - expected.setId(SOME_SHARE_ID); - expected.setMembershipId(SOME_MEMBERSHIP_ID); - expected.setRemark("Updated Remark"); - expected.setMembershipDisplayLabel(SOME_MEMBERSHIP_DISPLAY_LABEL); - assertThat(actual).isEqualToIgnoringGivenFields(expected, "displayLabel"); - } - - // --- only test fixture below --- - - private String createExpectedJSon(ShareDTO dto) { - return new JSonBuilder() - .withFieldValueIfPresent("id", dto.getId()) - .withFieldValueIfPresent("documentDate", dto.getDocumentDate().toString()) - .withFieldValueIfPresent("valueDate", dto.getValueDate().toString()) - .withFieldValueIfPresent("action", dto.getAction().name()) - .withFieldValueIfPresent("quantity", dto.getQuantity()) - .withFieldValueIfPresent("remark", dto.getRemark()) - .withFieldValueIfPresent("membershipId", dto.getMembershipId()) - .withFieldValue("membershipDisplayLabel", dto.getMembershipDisplayLabel()) - .toString(); - } - - private ShareDTO createSomeShareDTO(final long id) { - final ShareDTO given = new ShareDTO(); - given.setId(id); - given.setAction(ShareAction.SUBSCRIPTION); - given.setQuantity(16); - given.setDocumentDate(LocalDate.parse("2019-04-27")); - given.setValueDate(LocalDate.parse("2019-04-28")); - given.setMembershipId(SOME_MEMBERSHIP_ID); - given.setRemark("Some Remark"); - given.setMembershipDisplayLabel("Display Label for Membership #" + SOME_MEMBERSHIP_ID); - return given; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOUnitTest.java deleted file mode 100644 index 17b79e77..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOUnitTest.java +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.Admin; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerTechnicalContact; -import org.hostsharing.hsadminng.service.util.RandomUtil; - -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.RandomUtils; -import org.junit.Test; - -import java.time.LocalDate; - -public class ShareDTOUnitTest extends AccessMappingsUnitTestBase { - - public ShareDTOUnitTest() { - super(ShareDTO.class, ShareDTOUnitTest::createSampleDTO, ShareDTOUnitTest::createRandomDTO); - } - - @Test - public void shouldHaveProperAccessForAdmin() { - initAccessFor(ShareDTO.class, Admin.ROLE).shouldBeExactlyFor( - "membershipId", - "documentDate", - "quantity", - "action", - "valueDate", - "remark"); - updateAccessFor(ShareDTO.class, Admin.ROLE).shouldBeExactlyFor("remark"); - readAccessFor(ShareDTO.class, Admin.ROLE).shouldBeForAllFields(); - } - - @Test - public void shouldHaveProperAccessForContractualContact() { - initAccessFor(ShareDTO.class, CustomerContractualContact.ROLE).shouldBeForNothing(); - updateAccessFor(ShareDTO.class, CustomerContractualContact.ROLE).shouldBeForNothing(); - readAccessFor(ShareDTO.class, CustomerContractualContact.ROLE).shouldBeExactlyFor( - "id", - "membershipId", - "documentDate", - "quantity", - "action", - "valueDate", - "membershipDisplayLabel"); - } - - @Test - public void shouldHaveNoAccessForTechnicalContact() { - initAccessFor(ShareDTO.class, CustomerTechnicalContact.ROLE).shouldBeForNothing(); - updateAccessFor(ShareDTO.class, CustomerTechnicalContact.ROLE).shouldBeForNothing(); - readAccessFor(ShareDTO.class, CustomerTechnicalContact.ROLE).shouldBeForNothing(); - } - - @Test - public void shouldHaveNoAccessForNormalUsersWithinCustomerRealm() { - initAccessFor(ShareDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - updateAccessFor(ShareDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - readAccessFor(ShareDTO.class, Role.AnyCustomerUser.ROLE).shouldBeForNothing(); - } - - // --- only test fixture below --- - - private static ShareDTO createSampleDTO(final Long id, final Long parentId) { - final ShareDTO dto = new ShareDTO(); - dto.setId(id); - dto.setMembershipId(parentId); - dto.setAction(ShareAction.SUBSCRIPTION); - dto.setQuantity(3); - dto.setDocumentDate(LocalDate.parse("2019-04-22")); - dto.setValueDate(LocalDate.parse("2019-04-30")); - dto.setRemark("Some Remark"); - dto.setMembershipDisplayLabel("The Membership #888"); - return dto; - } - - private static ShareDTO createRandomDTO(final Long id, final Long parentId) { - final ShareDTO dto = new ShareDTO(); - dto.setId(id); - dto.setMembershipId(parentId); - dto.setAction(RandomUtil.generateEnumValue(ShareAction.class)); - dto.setQuantity(RandomUtils.nextInt()); - final LocalDate randomDate = LocalDate.parse("2000-12-07").plusDays(RandomUtils.nextInt(1, 999)); - dto.setDocumentDate(randomDate); - dto.setValueDate(randomDate.plusDays(RandomUtils.nextInt(1, 99))); - dto.setRemark(RandomStringUtils.randomAlphanumeric(20)); - dto.setMembershipDisplayLabel("The Membership #" + dto.getMembershipId()); - return dto; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/UserRoleAssignmentUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/UserRoleAssignmentUnitTest.java deleted file mode 100644 index 5a22d68d..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/UserRoleAssignmentUnitTest.java +++ /dev/null @@ -1,163 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.dto; - -import static org.apache.commons.lang3.tuple.ImmutablePair.of; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.mockito.BDDMockito.given; - -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.domain.UserRoleAssignment; -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.repository.UserRoleAssignmentRepository; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.JSonBuilder; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit4.SpringRunner; - -import java.io.IOException; -import java.util.Optional; - -@JsonTest -@SpringBootTest( - classes = { - UserRoleAssignmentRepository.class, - UserRoleAssignmentService.class, - UserRoleAssignment.UserRoleAssignmentJsonSerializer.class, - UserRoleAssignment.UserRoleAssignmentJsonDeserializer.class }) -@RunWith(SpringRunner.class) -public class UserRoleAssignmentUnitTest { - - private static final long USER_ROLE_ASSIGNMENT_ID = 1234L; - private static final long CUSTOMER_ID = 888L; - private static final long USER_ID = 42L; - - @Rule - public MockitoRule mockito = MockitoJUnit.rule(); - - @Autowired - private ObjectMapper objectMapper; - - @MockBean - private UserRepository userRepository; - - @MockBean - private UserRoleAssignmentRepository userRoleAssignmentRepository; - - @MockBean - private UserRoleAssignmentService userRoleAssignmentService; - - private SecurityContextMock securityContext; - - @Before - public void init() { - securityContext = SecurityContextMock.usingMock(userRoleAssignmentService); - } - - @Test - public void testSerializationAsContractualCustomerContact() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser() - .withRole( - CustomerDTO.class, - CUSTOMER_ID, - Role.CustomerContractualContact.ROLE); - UserRoleAssignment given = createSomeUserRoleAssignment(USER_ROLE_ASSIGNMENT_ID); - - // when - String actual = objectMapper.writeValueAsString(given); - - // then - assertEquals("{}", actual); // dependent rights not yet implemented for UserRoleAssignments - } - - @Test - public void testSerializationAsSupporter() throws JsonProcessingException { - - // given - securityContext.havingAuthenticatedUser().withAuthority(Role.Supporter.ROLE.authority()); - UserRoleAssignment given = createSomeUserRoleAssignment(USER_ROLE_ASSIGNMENT_ID); - - // when - String actual = objectMapper.writeValueAsString(given); - - // then - assertThat(actual).isEqualTo(createExpectedJSon(given)); - } - - @Test - public void testDeserializeAsAdmin() throws IOException { - // given - securityContext.havingAuthenticatedUser().withAuthority(Role.Admin.ROLE.authority()); - given(userRoleAssignmentRepository.findById(USER_ROLE_ASSIGNMENT_ID)) - .willReturn(Optional.of(new UserRoleAssignment().id(USER_ROLE_ASSIGNMENT_ID))); - final User expectedUser = new User().id(USER_ID); - given(userRepository.getOne(USER_ID)).willReturn(expectedUser); - String json = JSonBuilder.asJSon( - of("id", USER_ROLE_ASSIGNMENT_ID), - of("entityTypeId", Customer.ENTITY_TYPE_ID), - of("entityObjectId", CUSTOMER_ID), - of( - "user", - JSonBuilder.asJSon( - of("id", USER_ID))), - of("assignedRole", Role.CustomerTechnicalContact.ROLE.name())); - - // when - UserRoleAssignment actual = objectMapper.readValue(json, UserRoleAssignment.class); - - // then - UserRoleAssignment expected = new UserRoleAssignment(); - expected.setId(USER_ROLE_ASSIGNMENT_ID); - expected.setEntityTypeId(Customer.ENTITY_TYPE_ID); - expected.setEntityObjectId(CUSTOMER_ID); - expected.setAssignedRole(Role.CustomerTechnicalContact.ROLE); - expected.setUser(expectedUser); - assertThat(actual).isEqualToComparingFieldByField(expected); - } - - @Test - public void getAssignedRoleHandlesNullValue() { - assertThat(new UserRoleAssignment().assignedRole(null).getAssignedRole()).isNull(); - assertThat(new UserRoleAssignment().assignedRole(Role.Admin.ROLE).getAssignedRole()).isEqualTo(Role.Admin.ROLE); - } - - // --- only test fixture below --- - - public static String createExpectedJSon(UserRoleAssignment dto) { - return new JSonBuilder() - .withFieldValueIfPresent("id", dto.getId()) - .withFieldValueIfPresent("entityTypeId", dto.getEntityTypeId()) - .withFieldValueIfPresent("entityObjectId", dto.getEntityObjectId()) - .withFieldValueIfPresent("assignedRole", dto.getAssignedRole()) - .withFieldValueIfPresent("user", dto.getUser().getId()) - .toString(); - } - - public static UserRoleAssignment createSomeUserRoleAssignment(final Long id) { - final UserRoleAssignment given = new UserRoleAssignment(); - given.setId(id); - given.setEntityTypeId(Customer.ENTITY_TYPE_ID); - given.setEntityObjectId(CUSTOMER_ID); - given.setUser(new User().id(USER_ID)); - given.setAssignedRole(Role.CustomerTechnicalContact.ROLE); - return given; - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/mapper/UserMapperTest.java b/src/test/java/org/hostsharing/hsadminng/service/mapper/UserMapperTest.java deleted file mode 100644 index 69bd23c3..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/mapper/UserMapperTest.java +++ /dev/null @@ -1,151 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.mapper; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.service.dto.UserDTO; - -import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Test class for the UserMapper. - * - * @see UserMapper - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class UserMapperTest { - - private static final String DEFAULT_LOGIN = "johndoe"; - - @Autowired - private UserMapper userMapper; - - private User user; - private UserDTO userDto; - - private static final Long DEFAULT_ID = 1L; - - @Before - public void init() { - user = new User(); - user.setLogin(DEFAULT_LOGIN); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(true); - user.setEmail("johndoe@localhost"); - user.setFirstName("john"); - user.setLastName("doe"); - user.setImageUrl("image_url"); - user.setLangKey("en"); - - userDto = new UserDTO(user); - } - - @Test - public void usersToUserDTOsShouldMapOnlyNonNullUsers() { - List users = new ArrayList<>(); - users.add(user); - users.add(null); - - List userDTOS = userMapper.usersToUserDTOs(users); - - assertThat(userDTOS).isNotEmpty(); - assertThat(userDTOS).size().isEqualTo(1); - } - - @Test - public void userDTOsToUsersShouldMapOnlyNonNullUsers() { - List usersDto = new ArrayList<>(); - usersDto.add(userDto); - usersDto.add(null); - - List users = userMapper.userDTOsToUsers(usersDto); - - assertThat(users).isNotEmpty(); - assertThat(users).size().isEqualTo(1); - } - - @Test - public void userDTOsToUsersWithAuthoritiesStringShouldMapToUsersWithAuthoritiesDomain() { - Set authoritiesAsString = new HashSet<>(); - authoritiesAsString.add("ADMIN"); - userDto.setAuthorities(authoritiesAsString); - - List usersDto = new ArrayList<>(); - usersDto.add(userDto); - - List users = userMapper.userDTOsToUsers(usersDto); - - assertThat(users).isNotEmpty(); - assertThat(users).size().isEqualTo(1); - assertThat(users.get(0).getAuthorities()).isNotNull(); - assertThat(users.get(0).getAuthorities()).isNotEmpty(); - assertThat(users.get(0).getAuthorities().iterator().next().getName()).isEqualTo("ADMIN"); - } - - @Test - public void userDTOsToUsersMapWithNullAuthoritiesStringShouldReturnUserWithEmptyAuthorities() { - userDto.setAuthorities(null); - - List usersDto = new ArrayList<>(); - usersDto.add(userDto); - - List users = userMapper.userDTOsToUsers(usersDto); - - assertThat(users).isNotEmpty(); - assertThat(users).size().isEqualTo(1); - assertThat(users.get(0).getAuthorities()).isNotNull(); - assertThat(users.get(0).getAuthorities()).isEmpty(); - } - - @Test - public void userDTOToUserMapWithAuthoritiesStringShouldReturnUserWithAuthorities() { - Set authoritiesAsString = new HashSet<>(); - authoritiesAsString.add("ADMIN"); - userDto.setAuthorities(authoritiesAsString); - - userDto.setAuthorities(authoritiesAsString); - - User user = userMapper.userDTOToUser(userDto); - - assertThat(user).isNotNull(); - assertThat(user.getAuthorities()).isNotNull(); - assertThat(user.getAuthorities()).isNotEmpty(); - assertThat(user.getAuthorities().iterator().next().getName()).isEqualTo("ADMIN"); - } - - @Test - public void userDTOToUserMapWithNullAuthoritiesStringShouldReturnUserWithEmptyAuthorities() { - userDto.setAuthorities(null); - - User user = userMapper.userDTOToUser(userDto); - - assertThat(user).isNotNull(); - assertThat(user.getAuthorities()).isNotNull(); - assertThat(user.getAuthorities()).isEmpty(); - } - - @Test - public void userDTOToUserMapWithNullUserShouldReturnNull() { - assertThat(userMapper.userDTOToUser(null)).isNull(); - } - - @Test - public void testUserFromId() { - assertThat(userMapper.userFromId(DEFAULT_ID).getId()).isEqualTo(DEFAULT_ID); - assertThat(userMapper.userFromId(null)).isNull(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/service/util/ReflectionUtilUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/util/ReflectionUtilUnitTest.java deleted file mode 100644 index 43013560..00000000 --- a/src/test/java/org/hostsharing/hsadminng/service/util/ReflectionUtilUnitTest.java +++ /dev/null @@ -1,147 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.service.util; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.ThrowableAssert.catchThrowable; -import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked; - -import org.junit.Test; - -public class ReflectionUtilUnitTest { - - @Test - public void getUknownFieldThrowsIllegalArgumentException() { - final Throwable actual = catchThrowable(() -> ReflectionUtil.getField(SomeClass.class, "unknownField")); - assertThat(actual).isInstanceOf(IllegalArgumentException.class) - .hasMessage("java.lang.NoSuchFieldException: unknownField"); - } - - @Test - public void setValue() { - final TestDto dto = new TestDto(5); - ReflectionUtil.setValue(dto, ReflectionUtil.getField(dto.getClass(), "intVal"), 77); - assertThat(dto.intVal).isEqualTo(77); - } - - @Test - public void setValueViaSuperclass() { - final SubTestDto dto = new SubTestDto(5); - ReflectionUtil.setValue(dto, ReflectionUtil.getField(dto.getClass(), "intVal"), 77); - assertThat(dto.intVal).isEqualTo(77); - } - - @Test - public void getValue() { - final TestDto dto = new TestDto(5); - final int actual = ReflectionUtil.getValue(dto, ReflectionUtil.getField(dto.getClass(), "intVal")); - assertThat(actual).isEqualTo(5); - } - - @Test - public void getValueViaSuperclass() { - final SubTestDto dto = new SubTestDto(5); - final int actual = ReflectionUtil.getValue(dto, ReflectionUtil.getField(dto.getClass(), "intVal")); - assertThat(actual).isEqualTo(5); - } - - @Test - public void determineGenericInterfaceParameteDirect() { - Class actual = ReflectionUtil.determineGenericInterfaceParameter(SuperClass.class, GenericInterface.class, 1); - assertThat(actual).isEqualTo(Long.class); - } - - @Test - public void determineGenericInterfaceParameterViaSuperclass() { - Class actual = ReflectionUtil.determineGenericInterfaceParameter(SomeClass.class, GenericInterface.class, 1); - assertThat(actual).isEqualTo(Long.class); - } - - @Test - public void throwsExceptionIfGenericInterfaceNotImplemented() { - final Throwable actual = catchThrowable( - () -> ReflectionUtil.determineGenericInterfaceParameter(SomeClass.class, UnusedGenericInterface.class, 1)); - assertThat(actual).isInstanceOf(AssertionError.class) - .hasMessageContaining("SomeClass expected to implement UnusedGenericInterface<...>"); - } - - @Test - public void determineGenericClassParameterDirect() { - Class actual = ReflectionUtil.determineGenericClassParameter(SuperClass.class, GenericClass.class, 1); - assertThat(actual).isEqualTo(Boolean.class); - } - - @Test - public void determineGenericClassParameterViaSuperclss() { - Class actual = ReflectionUtil.determineGenericClassParameter(SomeClass.class, GenericClass.class, 1); - assertThat(actual).isEqualTo(Boolean.class); - } - - @Test - public void throwsExceptionIfGenericClassNotExended() { - final Throwable actual = catchThrowable( - () -> ReflectionUtil.determineGenericClassParameter(SomeClass.class, UnusedSuperClass.class, 1)); - assertThat(actual).isInstanceOf(AssertionError.class) - .hasMessageContaining("GenericClass expected to extend UnusedSuperClass<...>"); - } - - @Test - public void uncheckedRethrowsCheckedException() { - final Exception givenException = new Exception("Checked Test Exception"); - final Throwable actual = catchThrowable(() -> unchecked(() -> { - throw givenException; - })); - assertThat(actual) - .isInstanceOfSatisfying(RuntimeException.class, rte -> assertThat(rte.getCause()).isSameAs(givenException)); - } - - @Test - public void asEnumValue() { - assertThat(ReflectionUtil.asEnumValue(SomeEnum.class, "GREEN")).isEqualTo(SomeEnum.GREEN); - } - - // --- only test fixture below --- - - private static class TestDto { - - int intVal; - - TestDto(final int intval) { - this.intVal = intval; - } - } - - private static class SubTestDto extends TestDto { - - SubTestDto(final int intval) { - super(intval); - } - } - - private static class SomeClass extends SuperClass { - } - - private static class SuperClass extends GenericClass implements IntermediateInterfaces { - } - - private static class UnusedSuperClass extends GenericClass - implements IntermediateInterfaces { - } - - private static class GenericClass { - } - - private interface IntermediateInterfaces extends GenericInterface { - } - - private interface GenericInterface { - } - - private interface UnusedGenericInterface { - } - - enum SomeEnum { - RED, - BLUE, - GREEN - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/AccountResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/AccountResourceIntTest.java deleted file mode 100644 index dcaf6a6c..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/AccountResourceIntTest.java +++ /dev/null @@ -1,829 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.config.Constants; -import org.hostsharing.hsadminng.domain.Authority; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.repository.AuthorityRepository; -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.MailService; -import org.hostsharing.hsadminng.service.UserService; -import org.hostsharing.hsadminng.service.dto.PasswordChangeDTO; -import org.hostsharing.hsadminng.service.dto.UserDTO; -import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; -import org.hostsharing.hsadminng.web.rest.vm.KeyAndPasswordVM; -import org.hostsharing.hsadminng.web.rest.vm.ManagedUserVM; - -import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; -import java.util.*; - -/** - * Test class for the AccountResource REST controller. - * - * @see AccountResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class AccountResourceIntTest { - - @Autowired - private UserRepository userRepository; - - @Autowired - private AuthorityRepository authorityRepository; - - @Autowired - private UserService userService; - - @Autowired - private PasswordEncoder passwordEncoder; - - @Autowired - private HttpMessageConverter[] httpMessageConverters; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - @Mock - private UserService mockUserService; - - @Mock - private MailService mockMailService; - - private MockMvc restMvc; - - private MockMvc restUserMockMvc; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - doNothing().when(mockMailService).sendActivationEmail(any()); - AccountResource accountResource = new AccountResource(userRepository, userService, mockMailService); - - AccountResource accountUserMockResource = new AccountResource(userRepository, mockUserService, mockMailService); - this.restMvc = MockMvcBuilders.standaloneSetup(accountResource) - .setMessageConverters(httpMessageConverters) - .setControllerAdvice(exceptionTranslator) - .build(); - this.restUserMockMvc = MockMvcBuilders.standaloneSetup(accountUserMockResource) - .setControllerAdvice(exceptionTranslator) - .build(); - } - - @Test - public void testNonAuthenticatedUser() throws Exception { - restUserMockMvc.perform( - get("/api/authenticate") - .accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().string("")); - } - - @Test - public void testAuthenticatedUser() throws Exception { - restUserMockMvc.perform( - get("/api/authenticate") - .with(request -> { - request.setRemoteUser("test"); - return request; - }) - .accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().string("test")); - } - - @Test - public void testGetExistingAccount() throws Exception { - Set authorities = new HashSet<>(); - Authority authority = new Authority(); - authority.setName(AuthoritiesConstants.ADMIN); - authorities.add(authority); - - User user = new User(); - user.setLogin("test"); - user.setFirstName("john"); - user.setLastName("doe"); - user.setEmail("john.doe@jhipster.com"); - user.setImageUrl("http://placehold.it/50x50"); - user.setLangKey("en"); - user.setAuthorities(authorities); - when(mockUserService.getUserWithAuthorities()).thenReturn(Optional.of(user)); - - restUserMockMvc.perform( - get("/api/account") - .accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.login").value("test")) - .andExpect(jsonPath("$.firstName").value("john")) - .andExpect(jsonPath("$.lastName").value("doe")) - .andExpect(jsonPath("$.email").value("john.doe@jhipster.com")) - .andExpect(jsonPath("$.imageUrl").value("http://placehold.it/50x50")) - .andExpect(jsonPath("$.langKey").value("en")) - .andExpect(jsonPath("$.authorities").value(AuthoritiesConstants.ADMIN)); - } - - @Test - public void testGetUnknownAccount() throws Exception { - when(mockUserService.getUserWithAuthorities()).thenReturn(Optional.empty()); - - restUserMockMvc.perform( - get("/api/account") - .accept(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(status().isInternalServerError()); - } - - @Test - @Transactional - public void testRegisterValid() throws Exception { - ManagedUserVM validUser = new ManagedUserVM(); - validUser.setLogin("test-register-valid"); - validUser.setPassword("password"); - validUser.setFirstName("Alice"); - validUser.setLastName("Test"); - validUser.setEmail("test-register-valid@example.com"); - validUser.setImageUrl("http://placehold.it/50x50"); - validUser.setLangKey(Constants.DEFAULT_LANGUAGE); - validUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - assertThat(userRepository.findOneByLogin("test-register-valid").isPresent()).isFalse(); - - restMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(validUser))) - .andExpect(status().isCreated()); - - assertThat(userRepository.findOneByLogin("test-register-valid").isPresent()).isTrue(); - } - - @Test - @Transactional - public void testRegisterInvalidLogin() throws Exception { - ManagedUserVM invalidUser = new ManagedUserVM(); - invalidUser.setLogin("funky-log!n");// <-- invalid - invalidUser.setPassword("password"); - invalidUser.setFirstName("Funky"); - invalidUser.setLastName("One"); - invalidUser.setEmail("funky@example.com"); - invalidUser.setActivated(true); - invalidUser.setImageUrl("http://placehold.it/50x50"); - invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE); - invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - restUserMockMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(invalidUser))) - .andExpect(status().isBadRequest()); - - Optional user = userRepository.findOneByEmailIgnoreCase("funky@example.com"); - assertThat(user.isPresent()).isFalse(); - } - - @Test - @Transactional - public void testRegisterInvalidEmail() throws Exception { - ManagedUserVM invalidUser = new ManagedUserVM(); - invalidUser.setLogin("bob"); - invalidUser.setPassword("password"); - invalidUser.setFirstName("Bob"); - invalidUser.setLastName("Green"); - invalidUser.setEmail("invalid");// <-- invalid - invalidUser.setActivated(true); - invalidUser.setImageUrl("http://placehold.it/50x50"); - invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE); - invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - restUserMockMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(invalidUser))) - .andExpect(status().isBadRequest()); - - Optional user = userRepository.findOneByLogin("bob"); - assertThat(user.isPresent()).isFalse(); - } - - @Test - @Transactional - public void testRegisterInvalidPassword() throws Exception { - ManagedUserVM invalidUser = new ManagedUserVM(); - invalidUser.setLogin("bob"); - invalidUser.setPassword("123");// password with only 3 digits - invalidUser.setFirstName("Bob"); - invalidUser.setLastName("Green"); - invalidUser.setEmail("bob@example.com"); - invalidUser.setActivated(true); - invalidUser.setImageUrl("http://placehold.it/50x50"); - invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE); - invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - restUserMockMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(invalidUser))) - .andExpect(status().isBadRequest()); - - Optional user = userRepository.findOneByLogin("bob"); - assertThat(user.isPresent()).isFalse(); - } - - @Test - @Transactional - public void testRegisterNullPassword() throws Exception { - ManagedUserVM invalidUser = new ManagedUserVM(); - invalidUser.setLogin("bob"); - invalidUser.setPassword(null);// invalid null password - invalidUser.setFirstName("Bob"); - invalidUser.setLastName("Green"); - invalidUser.setEmail("bob@example.com"); - invalidUser.setActivated(true); - invalidUser.setImageUrl("http://placehold.it/50x50"); - invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE); - invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - restUserMockMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(invalidUser))) - .andExpect(status().isBadRequest()); - - Optional user = userRepository.findOneByLogin("bob"); - assertThat(user.isPresent()).isFalse(); - } - - @Test - @Transactional - public void testRegisterDuplicateLogin() throws Exception { - // First registration - ManagedUserVM firstUser = new ManagedUserVM(); - firstUser.setLogin("alice"); - firstUser.setPassword("password"); - firstUser.setFirstName("Alice"); - firstUser.setLastName("Something"); - firstUser.setEmail("alice@example.com"); - firstUser.setImageUrl("http://placehold.it/50x50"); - firstUser.setLangKey(Constants.DEFAULT_LANGUAGE); - firstUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - // Duplicate login, different email - ManagedUserVM secondUser = new ManagedUserVM(); - secondUser.setLogin(firstUser.getLogin()); - secondUser.setPassword(firstUser.getPassword()); - secondUser.setFirstName(firstUser.getFirstName()); - secondUser.setLastName(firstUser.getLastName()); - secondUser.setEmail("alice2@example.com"); - secondUser.setImageUrl(firstUser.getImageUrl()); - secondUser.setLangKey(firstUser.getLangKey()); - secondUser.setCreatedBy(firstUser.getCreatedBy()); - secondUser.setCreatedDate(firstUser.getCreatedDate()); - secondUser.setLastModifiedBy(firstUser.getLastModifiedBy()); - secondUser.setLastModifiedDate(firstUser.getLastModifiedDate()); - secondUser.setAuthorities(new HashSet<>(firstUser.getAuthorities())); - - // First user - restMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(firstUser))) - .andExpect(status().isCreated()); - - // Second (non activated) user - restMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(secondUser))) - .andExpect(status().isCreated()); - - Optional testUser = userRepository.findOneByEmailIgnoreCase("alice2@example.com"); - assertThat(testUser.isPresent()).isTrue(); - testUser.get().setActivated(true); - userRepository.save(testUser.get()); - - // Second (already activated) user - restMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(secondUser))) - .andExpect(status().is4xxClientError()); - } - - @Test - @Transactional - public void testRegisterDuplicateEmail() throws Exception { - // First user - ManagedUserVM firstUser = new ManagedUserVM(); - firstUser.setLogin("test-register-duplicate-email"); - firstUser.setPassword("password"); - firstUser.setFirstName("Alice"); - firstUser.setLastName("Test"); - firstUser.setEmail("test-register-duplicate-email@example.com"); - firstUser.setImageUrl("http://placehold.it/50x50"); - firstUser.setLangKey(Constants.DEFAULT_LANGUAGE); - firstUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - // Register first user - restMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(firstUser))) - .andExpect(status().isCreated()); - - Optional testUser1 = userRepository.findOneByLogin("test-register-duplicate-email"); - assertThat(testUser1.isPresent()).isTrue(); - - // Duplicate email, different login - ManagedUserVM secondUser = new ManagedUserVM(); - secondUser.setLogin("test-register-duplicate-email-2"); - secondUser.setPassword(firstUser.getPassword()); - secondUser.setFirstName(firstUser.getFirstName()); - secondUser.setLastName(firstUser.getLastName()); - secondUser.setEmail(firstUser.getEmail()); - secondUser.setImageUrl(firstUser.getImageUrl()); - secondUser.setLangKey(firstUser.getLangKey()); - secondUser.setAuthorities(new HashSet<>(firstUser.getAuthorities())); - - // Register second (non activated) user - restMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(secondUser))) - .andExpect(status().isCreated()); - - Optional testUser2 = userRepository.findOneByLogin("test-register-duplicate-email"); - assertThat(testUser2.isPresent()).isFalse(); - - Optional testUser3 = userRepository.findOneByLogin("test-register-duplicate-email-2"); - assertThat(testUser3.isPresent()).isTrue(); - - // Duplicate email - with uppercase email address - ManagedUserVM userWithUpperCaseEmail = new ManagedUserVM(); - userWithUpperCaseEmail.setId(firstUser.getId()); - userWithUpperCaseEmail.setLogin("test-register-duplicate-email-3"); - userWithUpperCaseEmail.setPassword(firstUser.getPassword()); - userWithUpperCaseEmail.setFirstName(firstUser.getFirstName()); - userWithUpperCaseEmail.setLastName(firstUser.getLastName()); - userWithUpperCaseEmail.setEmail("TEST-register-duplicate-email@example.com"); - userWithUpperCaseEmail.setImageUrl(firstUser.getImageUrl()); - userWithUpperCaseEmail.setLangKey(firstUser.getLangKey()); - userWithUpperCaseEmail.setAuthorities(new HashSet<>(firstUser.getAuthorities())); - - // Register third (not activated) user - restMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userWithUpperCaseEmail))) - .andExpect(status().isCreated()); - - Optional testUser4 = userRepository.findOneByLogin("test-register-duplicate-email-3"); - assertThat(testUser4.isPresent()).isTrue(); - assertThat(testUser4.get().getEmail()).isEqualTo("test-register-duplicate-email@example.com"); - - testUser4.get().setActivated(true); - userService.updateUser((new UserDTO(testUser4.get()))); - - // Register 4th (already activated) user - restMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(secondUser))) - .andExpect(status().is4xxClientError()); - } - - @Test - @Transactional - public void testRegisterAdminIsIgnored() throws Exception { - ManagedUserVM validUser = new ManagedUserVM(); - validUser.setLogin("badguy"); - validUser.setPassword("password"); - validUser.setFirstName("Bad"); - validUser.setLastName("Guy"); - validUser.setEmail("badguy@example.com"); - validUser.setActivated(true); - validUser.setImageUrl("http://placehold.it/50x50"); - validUser.setLangKey(Constants.DEFAULT_LANGUAGE); - validUser.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); - - restMvc.perform( - post("/api/register") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(validUser))) - .andExpect(status().isCreated()); - - Optional userDup = userRepository.findOneByLogin("badguy"); - assertThat(userDup.isPresent()).isTrue(); - assertThat(userDup.get().getAuthorities()).hasSize(1) - .containsExactly(authorityRepository.findById(AuthoritiesConstants.USER).get()); - } - - @Test - @Transactional - public void testActivateAccount() throws Exception { - final String activationKey = "some activation key"; - User user = new User(); - user.setLogin("activate-account"); - user.setEmail("activate-account@example.com"); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(false); - user.setActivationKey(activationKey); - - userRepository.saveAndFlush(user); - - restMvc.perform(get("/api/activate?key={activationKey}", activationKey)) - .andExpect(status().isOk()); - - user = userRepository.findOneByLogin(user.getLogin()).orElse(null); - assertThat(user.getActivated()).isTrue(); - } - - @Test - @Transactional - public void testActivateAccountWithWrongKey() throws Exception { - restMvc.perform(get("/api/activate?key=wrongActivationKey")) - .andExpect(status().isInternalServerError()); - } - - @Test - @Transactional - @WithMockUser("save-account") - public void testSaveAccount() throws Exception { - User user = new User(); - user.setLogin("save-account"); - user.setEmail("save-account@example.com"); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(true); - - userRepository.saveAndFlush(user); - - UserDTO userDTO = new UserDTO(); - userDTO.setLogin("not-used"); - userDTO.setFirstName("firstname"); - userDTO.setLastName("lastname"); - userDTO.setEmail("save-account@example.com"); - userDTO.setActivated(false); - userDTO.setImageUrl("http://placehold.it/50x50"); - userDTO.setLangKey(Constants.DEFAULT_LANGUAGE); - userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); - - restMvc.perform( - post("/api/account") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userDTO))) - .andExpect(status().isOk()); - - User updatedUser = userRepository.findOneByLogin(user.getLogin()).orElse(null); - assertThat(updatedUser.getFirstName()).isEqualTo(userDTO.getFirstName()); - assertThat(updatedUser.getLastName()).isEqualTo(userDTO.getLastName()); - assertThat(updatedUser.getEmail()).isEqualTo(userDTO.getEmail()); - assertThat(updatedUser.getLangKey()).isEqualTo(userDTO.getLangKey()); - assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword()); - assertThat(updatedUser.getImageUrl()).isEqualTo(userDTO.getImageUrl()); - assertThat(updatedUser.getActivated()).isEqualTo(true); - assertThat(updatedUser.getAuthorities()).isEmpty(); - } - - @Test - @Transactional - @WithMockUser("save-invalid-email") - public void testSaveInvalidEmail() throws Exception { - User user = new User(); - user.setLogin("save-invalid-email"); - user.setEmail("save-invalid-email@example.com"); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(true); - - userRepository.saveAndFlush(user); - - UserDTO userDTO = new UserDTO(); - userDTO.setLogin("not-used"); - userDTO.setFirstName("firstname"); - userDTO.setLastName("lastname"); - userDTO.setEmail("invalid email"); - userDTO.setActivated(false); - userDTO.setImageUrl("http://placehold.it/50x50"); - userDTO.setLangKey(Constants.DEFAULT_LANGUAGE); - userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); - - restMvc.perform( - post("/api/account") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userDTO))) - .andExpect(status().isBadRequest()); - - assertThat(userRepository.findOneByEmailIgnoreCase("invalid email")).isNotPresent(); - } - - @Test - @Transactional - @WithMockUser("save-existing-email") - public void testSaveExistingEmail() throws Exception { - User user = new User(); - user.setLogin("save-existing-email"); - user.setEmail("save-existing-email@example.com"); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(true); - - userRepository.saveAndFlush(user); - - User anotherUser = new User(); - anotherUser.setLogin("save-existing-email2"); - anotherUser.setEmail("save-existing-email2@example.com"); - anotherUser.setPassword(RandomStringUtils.random(60)); - anotherUser.setActivated(true); - - userRepository.saveAndFlush(anotherUser); - - UserDTO userDTO = new UserDTO(); - userDTO.setLogin("not-used"); - userDTO.setFirstName("firstname"); - userDTO.setLastName("lastname"); - userDTO.setEmail("save-existing-email2@example.com"); - userDTO.setActivated(false); - userDTO.setImageUrl("http://placehold.it/50x50"); - userDTO.setLangKey(Constants.DEFAULT_LANGUAGE); - userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); - - restMvc.perform( - post("/api/account") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userDTO))) - .andExpect(status().isBadRequest()); - - User updatedUser = userRepository.findOneByLogin("save-existing-email").orElse(null); - assertThat(updatedUser.getEmail()).isEqualTo("save-existing-email@example.com"); - } - - @Test - @Transactional - @WithMockUser("save-existing-email-and-login") - public void testSaveExistingEmailAndLogin() throws Exception { - User user = new User(); - user.setLogin("save-existing-email-and-login"); - user.setEmail("save-existing-email-and-login@example.com"); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(true); - - userRepository.saveAndFlush(user); - - UserDTO userDTO = new UserDTO(); - userDTO.setLogin("not-used"); - userDTO.setFirstName("firstname"); - userDTO.setLastName("lastname"); - userDTO.setEmail("save-existing-email-and-login@example.com"); - userDTO.setActivated(false); - userDTO.setImageUrl("http://placehold.it/50x50"); - userDTO.setLangKey(Constants.DEFAULT_LANGUAGE); - userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); - - restMvc.perform( - post("/api/account") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userDTO))) - .andExpect(status().isOk()); - - User updatedUser = userRepository.findOneByLogin("save-existing-email-and-login").orElse(null); - assertThat(updatedUser.getEmail()).isEqualTo("save-existing-email-and-login@example.com"); - } - - @Test - @Transactional - @WithMockUser("change-password-wrong-existing-password") - public void testChangePasswordWrongExistingPassword() throws Exception { - User user = new User(); - String currentPassword = RandomStringUtils.random(60); - user.setPassword(passwordEncoder.encode(currentPassword)); - user.setLogin("change-password-wrong-existing-password"); - user.setEmail("change-password-wrong-existing-password@example.com"); - userRepository.saveAndFlush(user); - - restMvc.perform( - post("/api/account/change-password") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content( - TestUtil.convertObjectToJsonBytes( - new PasswordChangeDTO("1" + currentPassword, "new password")))) - .andExpect(status().isBadRequest()); - - User updatedUser = userRepository.findOneByLogin("change-password-wrong-existing-password").orElse(null); - assertThat(passwordEncoder.matches("new password", updatedUser.getPassword())).isFalse(); - assertThat(passwordEncoder.matches(currentPassword, updatedUser.getPassword())).isTrue(); - } - - @Test - @Transactional - @WithMockUser("change-password") - public void testChangePassword() throws Exception { - User user = new User(); - String currentPassword = RandomStringUtils.random(60); - user.setPassword(passwordEncoder.encode(currentPassword)); - user.setLogin("change-password"); - user.setEmail("change-password@example.com"); - userRepository.saveAndFlush(user); - - restMvc.perform( - post("/api/account/change-password") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, "new password")))) - .andExpect(status().isOk()); - - User updatedUser = userRepository.findOneByLogin("change-password").orElse(null); - assertThat(passwordEncoder.matches("new password", updatedUser.getPassword())).isTrue(); - } - - @Test - @Transactional - @WithMockUser("change-password-too-small") - public void testChangePasswordTooSmall() throws Exception { - User user = new User(); - String currentPassword = RandomStringUtils.random(60); - user.setPassword(passwordEncoder.encode(currentPassword)); - user.setLogin("change-password-too-small"); - user.setEmail("change-password-too-small@example.com"); - userRepository.saveAndFlush(user); - - String newPassword = RandomStringUtils.random(ManagedUserVM.PASSWORD_MIN_LENGTH - 1); - - restMvc.perform( - post("/api/account/change-password") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, newPassword)))) - .andExpect(status().isBadRequest()); - - User updatedUser = userRepository.findOneByLogin("change-password-too-small").orElse(null); - assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword()); - } - - @Test - @Transactional - @WithMockUser("change-password-too-long") - public void testChangePasswordTooLong() throws Exception { - User user = new User(); - String currentPassword = RandomStringUtils.random(60); - user.setPassword(passwordEncoder.encode(currentPassword)); - user.setLogin("change-password-too-long"); - user.setEmail("change-password-too-long@example.com"); - userRepository.saveAndFlush(user); - - String newPassword = RandomStringUtils.random(ManagedUserVM.PASSWORD_MAX_LENGTH + 1); - - restMvc.perform( - post("/api/account/change-password") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, newPassword)))) - .andExpect(status().isBadRequest()); - - User updatedUser = userRepository.findOneByLogin("change-password-too-long").orElse(null); - assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword()); - } - - @Test - @Transactional - @WithMockUser("change-password-empty") - public void testChangePasswordEmpty() throws Exception { - User user = new User(); - String currentPassword = RandomStringUtils.random(60); - user.setPassword(passwordEncoder.encode(currentPassword)); - user.setLogin("change-password-empty"); - user.setEmail("change-password-empty@example.com"); - userRepository.saveAndFlush(user); - - restMvc.perform( - post("/api/account/change-password") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, "")))) - .andExpect(status().isBadRequest()); - - User updatedUser = userRepository.findOneByLogin("change-password-empty").orElse(null); - assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword()); - } - - @Test - @Transactional - public void testRequestPasswordReset() throws Exception { - User user = new User(); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(true); - user.setLogin("password-reset"); - user.setEmail("password-reset@example.com"); - userRepository.saveAndFlush(user); - - restMvc.perform( - post("/api/account/reset-password/init") - .content("password-reset@example.com")) - .andExpect(status().isOk()); - } - - @Test - @Transactional - public void testRequestPasswordResetUpperCaseEmail() throws Exception { - User user = new User(); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(true); - user.setLogin("password-reset"); - user.setEmail("password-reset@example.com"); - userRepository.saveAndFlush(user); - - restMvc.perform( - post("/api/account/reset-password/init") - .content("password-reset@EXAMPLE.COM")) - .andExpect(status().isOk()); - } - - @Test - public void testRequestPasswordResetWrongEmail() throws Exception { - restMvc.perform( - post("/api/account/reset-password/init") - .content("password-reset-wrong-email@example.com")) - .andExpect(status().isBadRequest()); - } - - @Test - @Transactional - public void testFinishPasswordReset() throws Exception { - User user = new User(); - user.setPassword(RandomStringUtils.random(60)); - user.setLogin("finish-password-reset"); - user.setEmail("finish-password-reset@example.com"); - user.setResetDate(Instant.now().plusSeconds(60)); - user.setResetKey("reset key"); - userRepository.saveAndFlush(user); - - KeyAndPasswordVM keyAndPassword = new KeyAndPasswordVM(); - keyAndPassword.setKey(user.getResetKey()); - keyAndPassword.setNewPassword("new password"); - - restMvc.perform( - post("/api/account/reset-password/finish") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(keyAndPassword))) - .andExpect(status().isOk()); - - User updatedUser = userRepository.findOneByLogin(user.getLogin()).orElse(null); - assertThat(passwordEncoder.matches(keyAndPassword.getNewPassword(), updatedUser.getPassword())).isTrue(); - } - - @Test - @Transactional - public void testFinishPasswordResetTooSmall() throws Exception { - User user = new User(); - user.setPassword(RandomStringUtils.random(60)); - user.setLogin("finish-password-reset-too-small"); - user.setEmail("finish-password-reset-too-small@example.com"); - user.setResetDate(Instant.now().plusSeconds(60)); - user.setResetKey("reset key too small"); - userRepository.saveAndFlush(user); - - KeyAndPasswordVM keyAndPassword = new KeyAndPasswordVM(); - keyAndPassword.setKey(user.getResetKey()); - keyAndPassword.setNewPassword("foo"); - - restMvc.perform( - post("/api/account/reset-password/finish") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(keyAndPassword))) - .andExpect(status().isBadRequest()); - - User updatedUser = userRepository.findOneByLogin(user.getLogin()).orElse(null); - assertThat(passwordEncoder.matches(keyAndPassword.getNewPassword(), updatedUser.getPassword())).isFalse(); - } - - @Test - @Transactional - public void testFinishPasswordResetWrongKey() throws Exception { - KeyAndPasswordVM keyAndPassword = new KeyAndPasswordVM(); - keyAndPassword.setKey("wrong reset key"); - keyAndPassword.setNewPassword("new password"); - - restMvc.perform( - post("/api/account/reset-password/finish") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(keyAndPassword))) - .andExpect(status().isInternalServerError()); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/AssetResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/AssetResourceIntTest.java deleted file mode 100644 index 30849f41..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/AssetResourceIntTest.java +++ /dev/null @@ -1,777 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.hasItem; -import static org.hostsharing.hsadminng.web.rest.TestUtil.createFormattingConversionService; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.domain.enumeration.AssetAction; -import org.hostsharing.hsadminng.repository.AssetRepository; -import org.hostsharing.hsadminng.service.AssetQueryService; -import org.hostsharing.hsadminng.service.AssetService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.dto.AssetDTO; -import org.hostsharing.hsadminng.service.mapper.AssetMapper; -import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.Validator; - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.List; - -import javax.persistence.EntityManager; - -/** - * Test class for the AssetResource REST controller. - * - * @see AssetResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class AssetResourceIntTest { - - private static final LocalDate DEFAULT_DOCUMENT_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_DOCUMENT_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final LocalDate DEFAULT_VALUE_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_VALUE_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final AssetAction DEFAULT_ACTION = AssetAction.PAYMENT; - private static final AssetAction UPDATED_ACTION = AssetAction.HANDOVER; - - private static final BigDecimal DEFAULT_AMOUNT = new BigDecimal("1"); - private static final BigDecimal UPDATED_AMOUNT = new BigDecimal("2"); - - private static final String DEFAULT_REMARK = "AAAAAAAAAA"; - private static final String UPDATED_REMARK = "BBBBBBBBBB"; - - @Autowired - private AssetRepository assetRepository; - - @Autowired - private AssetMapper assetMapper; - - @Autowired - private AssetService assetService; - - @Autowired - private AssetQueryService assetQueryService; - - @Autowired - private MappingJackson2HttpMessageConverter jacksonMessageConverter; - - @Autowired - private PageableHandlerMethodArgumentResolver pageableArgumentResolver; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - @Autowired - private EntityManager em; - - @Autowired - private Validator validator; - - @MockBean - private UserRoleAssignmentService userRoleAssignmentService; - - private MockMvc restAssetMockMvc; - - private Asset asset; - - @Before - public void setup() { - SecurityContextMock.usingMock(userRoleAssignmentService) - .havingAuthenticatedUser() - .withAuthority(Role.Admin.ROLE.authority()); - - MockitoAnnotations.initMocks(this); - final AssetResource assetResource = new AssetResource(assetService, assetQueryService); - this.restAssetMockMvc = MockMvcBuilders.standaloneSetup(assetResource) - .setCustomArgumentResolvers(pageableArgumentResolver) - .setControllerAdvice(exceptionTranslator) - .setConversionService(createFormattingConversionService()) - .setMessageConverters(jacksonMessageConverter) - .setValidator(validator) - .build(); - } - - /** - * Create an entity for this test. - * - * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static Asset createEntity(EntityManager em) { - Asset asset = new Asset() - .documentDate(DEFAULT_DOCUMENT_DATE) - .valueDate(DEFAULT_VALUE_DATE) - .action(DEFAULT_ACTION) - .amount(DEFAULT_AMOUNT) - .remark(DEFAULT_REMARK); - // Add required entity - Membership membership = MembershipResourceIntTest.createEntity(em); - em.persist(membership); - em.flush(); - asset.setMembership(membership); - return asset; - } - - /** - * Create a persistent entity related to the given persistent membership for testing purposes. - * - * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static Asset createPersistentEntity(EntityManager em, final Membership membership) { - Asset asset = new Asset() - .documentDate(DEFAULT_DOCUMENT_DATE) - .valueDate(DEFAULT_VALUE_DATE) - .action(DEFAULT_ACTION) - .amount(DEFAULT_AMOUNT) - .remark(DEFAULT_REMARK); - // Add required entity - asset.setMembership(membership); - membership.addAsset(asset); - em.persist(asset); - em.flush(); - return asset; - } - - @Before - public void initTest() { - asset = createEntity(em); - } - - @Test - @Transactional - public void createAsset() throws Exception { - int databaseSizeBeforeCreate = assetRepository.findAll().size(); - - // Create the Asset - AssetDTO assetDTO = assetMapper.toDto(asset); - assetDTO.setMembershipDisplayLabel(null); - restAssetMockMvc.perform( - post("/api/assets") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(assetDTO))) - .andExpect(status().isCreated()); - - // Validate the Asset in the database - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeCreate + 1); - Asset testAsset = assetList.get(assetList.size() - 1); - assertThat(testAsset.getDocumentDate()).isEqualTo(DEFAULT_DOCUMENT_DATE); - assertThat(testAsset.getValueDate()).isEqualTo(DEFAULT_VALUE_DATE); - assertThat(testAsset.getAction()).isEqualTo(DEFAULT_ACTION); - assertThat(testAsset.getAmount()).isEqualTo(DEFAULT_AMOUNT.setScale(2, RoundingMode.HALF_DOWN)); - assertThat(testAsset.getRemark()).isEqualTo(DEFAULT_REMARK); - } - - @Test - @Transactional - public void createAssetWithIdForNonExistingEntity() throws Exception { - int databaseSizeBeforeCreate = assetRepository.findAll().size(); - - // Create the Asset with an ID - asset.setId(1L); - AssetDTO assetDTO = assetMapper.toDto(asset); - - // An entity with an existing ID cannot be created, so this API call must fail - restAssetMockMvc.perform( - post("/api/assets") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(assetDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Asset in the database - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void createAssetWithExistingExistingEntity() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - int databaseSizeBeforeCreate = assetRepository.findAll().size(); - - // Create the Asset with the ID of an existing ID - AssetDTO assetDTO = assetMapper.toDto(asset); - - // An entity with an existing ID cannot be created, so this API call must fail - restAssetMockMvc.perform( - post("/api/assets") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(assetDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Asset in the database - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void checkDocumentDateIsRequired() throws Exception { - int databaseSizeBeforeTest = assetRepository.findAll().size(); - // set the field null - asset.setDocumentDate(null); - - // Create the Asset, which fails. - AssetDTO assetDTO = assetMapper.toDto(asset); - - restAssetMockMvc.perform( - post("/api/assets") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(assetDTO))) - .andExpect(status().isBadRequest()); - - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkValueDateIsRequired() throws Exception { - int databaseSizeBeforeTest = assetRepository.findAll().size(); - // set the field null - asset.setValueDate(null); - - // Create the Asset, which fails. - AssetDTO assetDTO = assetMapper.toDto(asset); - - restAssetMockMvc.perform( - post("/api/assets") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(assetDTO))) - .andExpect(status().isBadRequest()); - - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkActionIsRequired() throws Exception { - int databaseSizeBeforeTest = assetRepository.findAll().size(); - // set the field null - asset.setAction(null); - - // Create the Asset, which fails. - AssetDTO assetDTO = assetMapper.toDto(asset); - - restAssetMockMvc.perform( - post("/api/assets") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(assetDTO))) - .andExpect(status().isBadRequest()); - - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkAmountIsRequired() throws Exception { - int databaseSizeBeforeTest = assetRepository.findAll().size(); - // set the field null - asset.setAmount(null); - - // Create the Asset, which fails. - AssetDTO assetDTO = assetMapper.toDto(asset); - - restAssetMockMvc.perform( - post("/api/assets") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(assetDTO))) - .andExpect(status().isBadRequest()); - - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void getAllAssets() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList - restAssetMockMvc.perform(get("/api/assets?sort=id,desc")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(asset.getId().intValue()))) - .andExpect(jsonPath("$.[*].documentDate").value(hasItem(DEFAULT_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].valueDate").value(hasItem(DEFAULT_VALUE_DATE.toString()))) - .andExpect(jsonPath("$.[*].action").value(hasItem(DEFAULT_ACTION.toString()))) - .andExpect(jsonPath("$.[*].amount").value(hasItem(DEFAULT_AMOUNT.intValue()))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK))); - } - - @Test - @Transactional - public void getAsset() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get the asset - restAssetMockMvc.perform(get("/api/assets/{id}", asset.getId())) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.id").value(asset.getId().intValue())) - .andExpect(jsonPath("$.documentDate").value(DEFAULT_DOCUMENT_DATE.toString())) - .andExpect(jsonPath("$.valueDate").value(DEFAULT_VALUE_DATE.toString())) - .andExpect(jsonPath("$.action").value(DEFAULT_ACTION.toString())) - .andExpect(jsonPath("$.amount").value(DEFAULT_AMOUNT.intValue())) - .andExpect(jsonPath("$.remark").value(DEFAULT_REMARK)); - } - - @Test - @Transactional - public void getAllAssetsByDocumentDateIsEqualToSomething() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where documentDate equals to DEFAULT_DOCUMENT_DATE - defaultAssetShouldBeFound("documentDate.equals=" + DEFAULT_DOCUMENT_DATE); - - // Get all the assetList where documentDate equals to UPDATED_DOCUMENT_DATE - defaultAssetShouldNotBeFound("documentDate.equals=" + UPDATED_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllAssetsByDocumentDateIsInShouldWork() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where documentDate in DEFAULT_DOCUMENT_DATE or UPDATED_DOCUMENT_DATE - defaultAssetShouldBeFound("documentDate.in=" + DEFAULT_DOCUMENT_DATE + "," + UPDATED_DOCUMENT_DATE); - - // Get all the assetList where documentDate equals to UPDATED_DOCUMENT_DATE - defaultAssetShouldNotBeFound("documentDate.in=" + UPDATED_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllAssetsByDocumentDateIsNullOrNotNull() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where documentDate is not null - defaultAssetShouldBeFound("documentDate.specified=true"); - - // Get all the assetList where documentDate is null - defaultAssetShouldNotBeFound("documentDate.specified=false"); - } - - @Test - @Transactional - public void getAllAssetsByDocumentDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where documentDate greater than or equals to DEFAULT_DOCUMENT_DATE - defaultAssetShouldBeFound("documentDate.greaterOrEqualThan=" + DEFAULT_DOCUMENT_DATE); - - // Get all the assetList where documentDate greater than or equals to UPDATED_DOCUMENT_DATE - defaultAssetShouldNotBeFound("documentDate.greaterOrEqualThan=" + UPDATED_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllAssetsByDocumentDateIsLessThanSomething() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where documentDate less than or equals to DEFAULT_DOCUMENT_DATE - defaultAssetShouldNotBeFound("documentDate.lessThan=" + DEFAULT_DOCUMENT_DATE); - - // Get all the assetList where documentDate less than or equals to UPDATED_DOCUMENT_DATE - defaultAssetShouldBeFound("documentDate.lessThan=" + UPDATED_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllAssetsByValueDateIsEqualToSomething() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where valueDate equals to DEFAULT_VALUE_DATE - defaultAssetShouldBeFound("valueDate.equals=" + DEFAULT_VALUE_DATE); - - // Get all the assetList where valueDate equals to UPDATED_VALUE_DATE - defaultAssetShouldNotBeFound("valueDate.equals=" + UPDATED_VALUE_DATE); - } - - @Test - @Transactional - public void getAllAssetsByValueDateIsInShouldWork() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where valueDate in DEFAULT_VALUE_DATE or UPDATED_VALUE_DATE - defaultAssetShouldBeFound("valueDate.in=" + DEFAULT_VALUE_DATE + "," + UPDATED_VALUE_DATE); - - // Get all the assetList where valueDate equals to UPDATED_VALUE_DATE - defaultAssetShouldNotBeFound("valueDate.in=" + UPDATED_VALUE_DATE); - } - - @Test - @Transactional - public void getAllAssetsByValueDateIsNullOrNotNull() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where valueDate is not null - defaultAssetShouldBeFound("valueDate.specified=true"); - - // Get all the assetList where valueDate is null - defaultAssetShouldNotBeFound("valueDate.specified=false"); - } - - @Test - @Transactional - public void getAllAssetsByValueDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where valueDate greater than or equals to DEFAULT_VALUE_DATE - defaultAssetShouldBeFound("valueDate.greaterOrEqualThan=" + DEFAULT_VALUE_DATE); - - // Get all the assetList where valueDate greater than or equals to UPDATED_VALUE_DATE - defaultAssetShouldNotBeFound("valueDate.greaterOrEqualThan=" + UPDATED_VALUE_DATE); - } - - @Test - @Transactional - public void getAllAssetsByValueDateIsLessThanSomething() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where valueDate less than or equals to DEFAULT_VALUE_DATE - defaultAssetShouldNotBeFound("valueDate.lessThan=" + DEFAULT_VALUE_DATE); - - // Get all the assetList where valueDate less than or equals to UPDATED_VALUE_DATE - defaultAssetShouldBeFound("valueDate.lessThan=" + UPDATED_VALUE_DATE); - } - - @Test - @Transactional - public void getAllAssetsByActionIsEqualToSomething() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where action equals to DEFAULT_ACTION - defaultAssetShouldBeFound("action.equals=" + DEFAULT_ACTION); - - // Get all the assetList where action equals to UPDATED_ACTION - defaultAssetShouldNotBeFound("action.equals=" + UPDATED_ACTION); - } - - @Test - @Transactional - public void getAllAssetsByActionIsInShouldWork() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where action in DEFAULT_ACTION or UPDATED_ACTION - defaultAssetShouldBeFound("action.in=" + DEFAULT_ACTION + "," + UPDATED_ACTION); - - // Get all the assetList where action equals to UPDATED_ACTION - defaultAssetShouldNotBeFound("action.in=" + UPDATED_ACTION); - } - - @Test - @Transactional - public void getAllAssetsByActionIsNullOrNotNull() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where action is not null - defaultAssetShouldBeFound("action.specified=true"); - - // Get all the assetList where action is null - defaultAssetShouldNotBeFound("action.specified=false"); - } - - @Test - @Transactional - public void getAllAssetsByAmountIsEqualToSomething() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where amount equals to DEFAULT_AMOUNT - defaultAssetShouldBeFound("amount.equals=" + DEFAULT_AMOUNT); - - // Get all the assetList where amount equals to UPDATED_AMOUNT - defaultAssetShouldNotBeFound("amount.equals=" + UPDATED_AMOUNT); - } - - @Test - @Transactional - public void getAllAssetsByAmountIsInShouldWork() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where amount in DEFAULT_AMOUNT or UPDATED_AMOUNT - defaultAssetShouldBeFound("amount.in=" + DEFAULT_AMOUNT + "," + UPDATED_AMOUNT); - - // Get all the assetList where amount equals to UPDATED_AMOUNT - defaultAssetShouldNotBeFound("amount.in=" + UPDATED_AMOUNT); - } - - @Test - @Transactional - public void getAllAssetsByAmountIsNullOrNotNull() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where amount is not null - defaultAssetShouldBeFound("amount.specified=true"); - - // Get all the assetList where amount is null - defaultAssetShouldNotBeFound("amount.specified=false"); - } - - @Test - @Transactional - public void getAllAssetsByRemarkIsEqualToSomething() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where remark equals to DEFAULT_REMARK - defaultAssetShouldBeFound("remark.equals=" + DEFAULT_REMARK); - - // Get all the assetList where remark equals to UPDATED_REMARK - defaultAssetShouldNotBeFound("remark.equals=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllAssetsByRemarkIsInShouldWork() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where remark in DEFAULT_REMARK or UPDATED_REMARK - defaultAssetShouldBeFound("remark.in=" + DEFAULT_REMARK + "," + UPDATED_REMARK); - - // Get all the assetList where remark equals to UPDATED_REMARK - defaultAssetShouldNotBeFound("remark.in=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllAssetsByRemarkIsNullOrNotNull() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - // Get all the assetList where remark is not null - defaultAssetShouldBeFound("remark.specified=true"); - - // Get all the assetList where remark is null - defaultAssetShouldNotBeFound("remark.specified=false"); - } - - @Test - @Transactional - public void getAllAssetsByMembershipIsEqualToSomething() throws Exception { - // Initialize the database - Membership membership = MembershipResourceIntTest - .createPersistentEntity(em, CustomerResourceIntTest.createPersistentEntity(em)); - asset.setMembership(membership); - assetRepository.saveAndFlush(asset); - Long membershipId = membership.getId(); - - // Get all the assetList where membership equals to membershipId - defaultAssetShouldBeFound("membershipId.equals=" + membershipId); - - // Get all the assetList where membership equals to membershipId + 1 - defaultAssetShouldNotBeFound("membershipId.equals=" + (membershipId + 1)); - } - - /** - * Executes the search, and checks that the default entity is returned - */ - private void defaultAssetShouldBeFound(String filter) throws Exception { - restAssetMockMvc.perform(get("/api/assets?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(asset.getId().intValue()))) - .andExpect(jsonPath("$.[*].documentDate").value(hasItem(DEFAULT_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].valueDate").value(hasItem(DEFAULT_VALUE_DATE.toString()))) - .andExpect(jsonPath("$.[*].action").value(hasItem(DEFAULT_ACTION.toString()))) - .andExpect(jsonPath("$.[*].amount").value(hasItem(DEFAULT_AMOUNT.intValue()))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK))); - - // Check, that the count call also returns 1 - restAssetMockMvc.perform(get("/api/assets/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("1")); - } - - /** - * Executes the search, and checks that the default entity is not returned - */ - private void defaultAssetShouldNotBeFound(String filter) throws Exception { - restAssetMockMvc.perform(get("/api/assets?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$").isArray()) - .andExpect(jsonPath("$").isEmpty()); - - // Check, that the count call also returns 0 - restAssetMockMvc.perform(get("/api/assets/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("0")); - } - - @Test - @Transactional - public void getNonExistingAsset() throws Exception { - // Get the asset - restAssetMockMvc.perform(get("/api/assets/{id}", Long.MAX_VALUE)) - .andExpect(status().isNotFound()); - } - - @Test - @Transactional - public void updateAsset() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - int databaseSizeBeforeUpdate = assetRepository.findAll().size(); - - // Update the asset - Asset updatedAsset = assetRepository.findById(asset.getId()).get(); - // Disconnect from session so that the updates on updatedAsset are not directly saved in db - em.detach(updatedAsset); - updatedAsset - .documentDate(UPDATED_DOCUMENT_DATE) - .valueDate(UPDATED_VALUE_DATE) - .action(UPDATED_ACTION) - .amount(UPDATED_AMOUNT) - .remark(UPDATED_REMARK); - AssetDTO assetDTO = assetMapper.toDto(updatedAsset); - - restAssetMockMvc.perform( - put("/api/assets") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(assetDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Asset in the database - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeUpdate); - Asset testAsset = assetList.get(assetList.size() - 1); - assertThat(testAsset.getDocumentDate()).isEqualTo(DEFAULT_DOCUMENT_DATE); - assertThat(testAsset.getValueDate()).isEqualTo(DEFAULT_VALUE_DATE); - assertThat(testAsset.getAction()).isEqualByComparingTo(DEFAULT_ACTION); - assertThat(testAsset.getAmount()).isEqualByComparingTo(DEFAULT_AMOUNT); - assertThat(testAsset.getRemark()).isEqualTo(DEFAULT_REMARK); - } - - @Test - @Transactional - public void updateNonExistingAsset() throws Exception { - int databaseSizeBeforeUpdate = assetRepository.findAll().size(); - - // Create the Asset - AssetDTO assetDTO = assetMapper.toDto(asset); - - // If the entity doesn't have an ID, it will throw BadRequestAlertException - restAssetMockMvc.perform( - put("/api/assets") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(assetDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Asset in the database - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeUpdate); - } - - @Test - @Transactional - public void deleteAsset() throws Exception { - // Initialize the database - assetRepository.saveAndFlush(asset); - - int databaseSizeBeforeDelete = assetRepository.findAll().size(); - - // Delete the asset - restAssetMockMvc.perform( - delete("/api/assets/{id}", asset.getId()) - .accept(TestUtil.APPLICATION_JSON_UTF8)) - .andExpect(status().isBadRequest()); - - // Validate the database still contains the same number of assets - List assetList = assetRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeDelete); - } - - @Test - @Transactional - public void equalsVerifier() throws Exception { - TestUtil.equalsVerifier(Asset.class); - Asset asset1 = new Asset(); - asset1.setId(1L); - Asset asset2 = new Asset(); - asset2.setId(asset1.getId()); - assertThat(asset1).isEqualTo(asset2); - asset2.setId(2L); - assertThat(asset1).isNotEqualTo(asset2); - asset1.setId(null); - assertThat(asset1).isNotEqualTo(asset2); - } - - @Test - @Transactional - public void dtoEqualsVerifier() throws Exception { - TestUtil.equalsVerifier(AssetDTO.class); - AssetDTO assetDTO1 = new AssetDTO(); - assetDTO1.setId(1L); - AssetDTO assetDTO2 = new AssetDTO(); - assertThat(assetDTO1).isNotEqualTo(assetDTO2); - assetDTO2.setId(assetDTO1.getId()); - assertThat(assetDTO1).isEqualTo(assetDTO2); - assetDTO2.setId(2L); - assertThat(assetDTO1).isNotEqualTo(assetDTO2); - assetDTO1.setId(null); - assertThat(assetDTO1).isNotEqualTo(assetDTO2); - } - - @Test - @Transactional - public void testEntityFromId() { - assertThat(assetMapper.fromId(42L).getId()).isEqualTo(42); - assertThat(assetMapper.fromId(null)).isNull(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/AuditResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/AuditResourceIntTest.java deleted file mode 100644 index b2ca196d..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/AuditResourceIntTest.java +++ /dev/null @@ -1,164 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.hamcrest.Matchers.hasItem; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.config.audit.AuditEventConverter; -import org.hostsharing.hsadminng.domain.PersistentAuditEvent; -import org.hostsharing.hsadminng.repository.PersistenceAuditEventRepository; -import org.hostsharing.hsadminng.service.AuditEventService; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.format.support.FormattingConversionService; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; - -/** - * Test class for the AuditResource REST controller. - * - * @see AuditResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -@Transactional -public class AuditResourceIntTest { - - private static final String SAMPLE_PRINCIPAL = "SAMPLE_PRINCIPAL"; - private static final String SAMPLE_TYPE = "SAMPLE_TYPE"; - private static final Instant SAMPLE_TIMESTAMP = Instant.parse("2015-08-04T10:11:30Z"); - private static final long SECONDS_PER_DAY = 60 * 60 * 24; - - @Autowired - private PersistenceAuditEventRepository auditEventRepository; - - @Autowired - private AuditEventConverter auditEventConverter; - - @Autowired - private MappingJackson2HttpMessageConverter jacksonMessageConverter; - - @Autowired - private FormattingConversionService formattingConversionService; - - @Autowired - private PageableHandlerMethodArgumentResolver pageableArgumentResolver; - - private PersistentAuditEvent auditEvent; - - private MockMvc restAuditMockMvc; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - AuditEventService auditEventService = new AuditEventService(auditEventRepository, auditEventConverter); - AuditResource auditResource = new AuditResource(auditEventService); - this.restAuditMockMvc = MockMvcBuilders.standaloneSetup(auditResource) - .setCustomArgumentResolvers(pageableArgumentResolver) - .setConversionService(formattingConversionService) - .setMessageConverters(jacksonMessageConverter) - .build(); - } - - @Before - public void initTest() { - auditEventRepository.deleteAll(); - auditEvent = new PersistentAuditEvent(); - auditEvent.setAuditEventType(SAMPLE_TYPE); - auditEvent.setPrincipal(SAMPLE_PRINCIPAL); - auditEvent.setAuditEventDate(SAMPLE_TIMESTAMP); - } - - @Test - public void getAllAudits() throws Exception { - // Initialize the database - auditEventRepository.save(auditEvent); - - // Get all the audits - restAuditMockMvc.perform(get("/management/audits")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].principal").value(hasItem(SAMPLE_PRINCIPAL))); - } - - @Test - public void getAudit() throws Exception { - // Initialize the database - auditEventRepository.save(auditEvent); - - // Get the audit - restAuditMockMvc.perform(get("/management/audits/{id}", auditEvent.getId())) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.principal").value(SAMPLE_PRINCIPAL)); - } - - @Test - public void getAuditsByDate() throws Exception { - // Initialize the database - auditEventRepository.save(auditEvent); - - // Generate dates for selecting audits by date, making sure the period will contain the audit - String fromDate = SAMPLE_TIMESTAMP.minusSeconds(SECONDS_PER_DAY).toString().substring(0, 10); - String toDate = SAMPLE_TIMESTAMP.plusSeconds(SECONDS_PER_DAY).toString().substring(0, 10); - - // Get the audit - restAuditMockMvc.perform(get("/management/audits?fromDate=" + fromDate + "&toDate=" + toDate)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].principal").value(hasItem(SAMPLE_PRINCIPAL))); - } - - @Test - public void getNonExistingAuditsByDate() throws Exception { - // Initialize the database - auditEventRepository.save(auditEvent); - - // Generate dates for selecting audits by date, making sure the period will not contain the sample audit - String fromDate = SAMPLE_TIMESTAMP.minusSeconds(2 * SECONDS_PER_DAY).toString().substring(0, 10); - String toDate = SAMPLE_TIMESTAMP.minusSeconds(SECONDS_PER_DAY).toString().substring(0, 10); - - // Query audits but expect no results - restAuditMockMvc.perform(get("/management/audits?fromDate=" + fromDate + "&toDate=" + toDate)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(header().string("X-Total-Count", "0")); - } - - @Test - public void getNonExistingAudit() throws Exception { - // Get the audit - restAuditMockMvc.perform(get("/management/audits/{id}", Long.MAX_VALUE)) - .andExpect(status().isNotFound()); - } - - @Test - @Transactional - public void testPersistentAuditEventEquals() throws Exception { - TestUtil.equalsVerifier(PersistentAuditEvent.class); - PersistentAuditEvent auditEvent1 = new PersistentAuditEvent(); - auditEvent1.setId(1L); - PersistentAuditEvent auditEvent2 = new PersistentAuditEvent(); - auditEvent2.setId(auditEvent1.getId()); - assertThat(auditEvent1).isEqualTo(auditEvent2); - auditEvent2.setId(2L); - assertThat(auditEvent1).isNotEqualTo(auditEvent2); - auditEvent1.setId(null); - assertThat(auditEvent1).isNotEqualTo(auditEvent2); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/CustomerResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/CustomerResourceIntTest.java deleted file mode 100644 index 0b3f1356..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/CustomerResourceIntTest.java +++ /dev/null @@ -1,1339 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.hasItem; -import static org.hostsharing.hsadminng.web.rest.TestUtil.createFormattingConversionService; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.domain.SepaMandate; -import org.hostsharing.hsadminng.domain.enumeration.CustomerKind; -import org.hostsharing.hsadminng.domain.enumeration.VatRegion; -import org.hostsharing.hsadminng.repository.CustomerRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.CustomerQueryService; -import org.hostsharing.hsadminng.service.CustomerService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.dto.CustomerDTO; -import org.hostsharing.hsadminng.service.mapper.CustomerMapper; -import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Bean; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.Validator; - -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.List; - -import javax.persistence.EntityManager; - -/** - * Test class for the CustomerResource REST controller. - * - * @see CustomerResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class CustomerResourceIntTest { - - private static final Integer DEFAULT_REFERENCE = 10000; - private static final Integer UPDATED_REFERENCE = 10001; - private static final Integer OTHER_REFERENCE_BASE = 11000; - - private static final String DEFAULT_PREFIX = "def"; - private static final String UPDATED_PREFIX = "new"; - private static final String OTHER_PREFIX_BASE = "o"; - - private static final String DEFAULT_NAME = "Default GmbH"; - private static final String UPDATED_NAME = "Updated Default GmbH"; - private static final String OTHER_NAME_BASE = "Other Corp."; - - private static final String DEFAULT_CONTRACTUAL_ADDRESS = "Default Address"; - private static final String UPDATED_CONTRACTUAL_ADDRESS = "Updated Address"; - private static final String OTHER_CONTRACTUAL_ADDRESS_BASE = "Other Street "; - - private static final String DEFAULT_CONTRACTUAL_SALUTATION = "Default Contractual Salutation"; - private static final String UPDATED_CONTRACTUAL_SALUTATION = "Update Contractual Salutation"; - - private static final String DEFAULT_BILLING_ADDRESS = "Default Billing Address"; - private static final String UPDATED_BILLING_ADDRESS = "Updated Billing Address"; - - private static final String DEFAULT_BILLING_SALUTATION = "Default Billing Salutation"; - private static final String UPDATED_BILLING_SALUTATION = "Updted Billing Salutation"; - - private static final String DEFAULT_REMARK = "Default Remark"; - private static final String UPDATED_REMARK = "Updated Remark"; - - private static final CustomerKind DEFAULT_KIND = CustomerKind.NATURAL; - private static final CustomerKind UPDATED_KIND = CustomerKind.LEGAL; - private static final CustomerKind OTHER_KIND = CustomerKind.LEGAL; - - private static final LocalDate DEFAULT_BIRTH_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_BIRTH_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final String DEFAULT_BIRTH_PLACE = "AAAAAAAAAA"; - private static final String UPDATED_BIRTH_PLACE = "BBBBBBBBBB"; - - private static final String DEFAULT_REGISTRATION_COURT = "AAAAAAAAAA"; - private static final String UPDATED_REGISTRATION_COURT = "BBBBBBBBBB"; - - private static final String DEFAULT_REGISTRATION_NUMBER = "AAAAAAAAAA"; - private static final String UPDATED_REGISTRATION_NUMBER = "BBBBBBBBBB"; - - private static final VatRegion DEFAULT_VAT_REGION = VatRegion.DOMESTIC; - private static final VatRegion UPDATED_VAT_REGION = VatRegion.EU; - private static final VatRegion OTHER_VAT_REGION = VatRegion.EU; - - private static final String DEFAULT_VAT_NUMBER = "AAAAAAAAAA"; - private static final String UPDATED_VAT_NUMBER = "BBBBBBBBBB"; - - private static int otherCounter = 0; - - @Bean - public SecurityEvaluationContextExtension securityEvaluationContextExtension() { - return new SecurityEvaluationContextExtension(); - } - - @Autowired - private CustomerRepository customerRepository; - - @Autowired - private CustomerMapper customerMapper; - - @Autowired - private CustomerService customerService; - - @Autowired - private CustomerQueryService customerQueryService; - - @Autowired - private MappingJackson2HttpMessageConverter jacksonMessageConverter; - - @Autowired - private PageableHandlerMethodArgumentResolver pageableArgumentResolver; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - @Autowired - private EntityManager em; - - @Autowired - private Validator validator; - - @MockBean - private UserRoleAssignmentService userRoleAssignmentService; - - private MockMvc restCustomerMockMvc; - - private Customer customer; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - SecurityContextMock.usingMock(userRoleAssignmentService) - .havingAuthenticatedUser() - .withAuthority(AuthoritiesConstants.ADMIN); - - final CustomerResource customerResource = new CustomerResource(customerService, customerQueryService); - this.restCustomerMockMvc = MockMvcBuilders.standaloneSetup(customerResource) - .setCustomArgumentResolvers(pageableArgumentResolver) - .setControllerAdvice(exceptionTranslator) - .setConversionService(createFormattingConversionService()) - .setMessageConverters(jacksonMessageConverter) - .setValidator(validator) - .build(); - } - - /** - * Create an entity for this test. - *

- * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static Customer createEntity(EntityManager em) { - Customer customer = new Customer() - .reference(DEFAULT_REFERENCE) - .prefix(DEFAULT_PREFIX) - .name(DEFAULT_NAME) - .kind(DEFAULT_KIND) - .birthDate(DEFAULT_BIRTH_DATE) - .birthPlace(DEFAULT_BIRTH_PLACE) - .registrationCourt(DEFAULT_REGISTRATION_COURT) - .registrationNumber(DEFAULT_REGISTRATION_NUMBER) - .vatRegion(DEFAULT_VAT_REGION) - .vatNumber(DEFAULT_VAT_NUMBER) - .contractualSalutation(DEFAULT_CONTRACTUAL_SALUTATION) - .contractualAddress(DEFAULT_CONTRACTUAL_ADDRESS) - .billingSalutation(DEFAULT_BILLING_SALUTATION) - .billingAddress(DEFAULT_BILLING_ADDRESS) - .remark(DEFAULT_REMARK); - return customer; - } - - /** - * Create another entity for tests. - *

- * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static Customer createPersistentEntity(EntityManager em) { - Customer customer = new Customer() - .reference(OTHER_REFERENCE_BASE + otherCounter) - .prefix(OTHER_PREFIX_BASE + String.format("%02d", otherCounter)) - .name(OTHER_NAME_BASE + otherCounter) - .kind(OTHER_KIND) - .vatRegion(OTHER_VAT_REGION) - .contractualAddress(OTHER_CONTRACTUAL_ADDRESS_BASE + otherCounter); - em.persist(customer); - em.flush(); - ++otherCounter; - return customer; - } - - @Before - public void initTest() { - customer = createEntity(em); - } - - @Test - @Transactional - public void createCustomer() throws Exception { - int databaseSizeBeforeCreate = customerRepository.findAll().size(); - - // Create the Customer - CustomerDTO customerDTO = customerMapper.toDto(customer); - restCustomerMockMvc.perform( - post("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isCreated()); - - // Validate the Customer in the database - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeCreate + 1); - Customer testCustomer = customerList.get(customerList.size() - 1); - assertThat(testCustomer.getReference()).isEqualTo(DEFAULT_REFERENCE); - assertThat(testCustomer.getPrefix()).isEqualTo(DEFAULT_PREFIX); - assertThat(testCustomer.getName()).isEqualTo(DEFAULT_NAME); - assertThat(testCustomer.getKind()).isEqualTo(DEFAULT_KIND); - assertThat(testCustomer.getBirthDate()).isEqualTo(DEFAULT_BIRTH_DATE); - assertThat(testCustomer.getBirthPlace()).isEqualTo(DEFAULT_BIRTH_PLACE); - assertThat(testCustomer.getRegistrationCourt()).isEqualTo(DEFAULT_REGISTRATION_COURT); - assertThat(testCustomer.getRegistrationNumber()).isEqualTo(DEFAULT_REGISTRATION_NUMBER); - assertThat(testCustomer.getVatRegion()).isEqualTo(DEFAULT_VAT_REGION); - assertThat(testCustomer.getVatNumber()).isEqualTo(DEFAULT_VAT_NUMBER); - assertThat(testCustomer.getContractualSalutation()).isEqualTo(DEFAULT_CONTRACTUAL_SALUTATION); - assertThat(testCustomer.getContractualAddress()).isEqualTo(DEFAULT_CONTRACTUAL_ADDRESS); - assertThat(testCustomer.getBillingSalutation()).isEqualTo(DEFAULT_BILLING_SALUTATION); - assertThat(testCustomer.getBillingAddress()).isEqualTo(DEFAULT_BILLING_ADDRESS); - assertThat(testCustomer.getRemark()).isEqualTo(DEFAULT_REMARK); - } - - @Test - @Transactional - public void createCustomerWithExistingIdIsRejected() throws Exception { - // Initialize the database - final long existingCustomerId = customerRepository.saveAndFlush(customer).getId(); - int databaseSizeBeforeCreate = customerRepository.findAll().size(); - - // Create the Customer with an existing ID - customer.setId(existingCustomerId); - CustomerDTO customerDTO = customerMapper.toDto(customer); - - // An entity with an existing ID cannot be created, so this API call must fail - restCustomerMockMvc.perform( - post("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Customer in the database - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void createCustomerWithNonExistingIdIsRejected() throws Exception { - int databaseSizeBeforeCreate = customerRepository.findAll().size(); - - // Create the Customer with an ID for which no entity exists - customer.setId(1L); - CustomerDTO customerDTO = customerMapper.toDto(customer); - - // An entity with an existing ID cannot be created, so this API call must fail - restCustomerMockMvc.perform( - post("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Customer in the database - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void checkReferenceIsRequired() throws Exception { - int databaseSizeBeforeTest = customerRepository.findAll().size(); - // set the field null - customer.setReference(null); - - // Create the Customer, which fails. - CustomerDTO customerDTO = customerMapper.toDto(customer); - - restCustomerMockMvc.perform( - post("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isBadRequest()); - - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkPrefixIsRequired() throws Exception { - int databaseSizeBeforeTest = customerRepository.findAll().size(); - // set the field null - customer.setPrefix(null); - - // Create the Customer, which fails. - CustomerDTO customerDTO = customerMapper.toDto(customer); - - restCustomerMockMvc.perform( - post("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isBadRequest()); - - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkNameIsRequired() throws Exception { - int databaseSizeBeforeTest = customerRepository.findAll().size(); - // set the field null - customer.setName(null); - - // Create the Customer, which fails. - CustomerDTO customerDTO = customerMapper.toDto(customer); - - restCustomerMockMvc.perform( - post("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isBadRequest()); - - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkKindIsRequired() throws Exception { - int databaseSizeBeforeTest = customerRepository.findAll().size(); - // set the field null - customer.setKind(null); - - // Create the Customer, which fails. - CustomerDTO customerDTO = customerMapper.toDto(customer); - - restCustomerMockMvc.perform( - post("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isBadRequest()); - - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkVatRegionIsRequired() throws Exception { - int databaseSizeBeforeTest = customerRepository.findAll().size(); - // set the field null - customer.setVatRegion(null); - - // Create the Customer, which fails. - CustomerDTO customerDTO = customerMapper.toDto(customer); - - restCustomerMockMvc.perform( - post("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isBadRequest()); - - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkContractualAddressIsRequired() throws Exception { - int databaseSizeBeforeTest = customerRepository.findAll().size(); - // set the field null - customer.setContractualAddress(null); - - // Create the Customer, which fails. - CustomerDTO customerDTO = customerMapper.toDto(customer); - - restCustomerMockMvc.perform( - post("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isBadRequest()); - - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void getAllCustomers() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList - restCustomerMockMvc.perform(get("/api/customers?sort=id,desc")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(customer.getId().intValue()))) - .andExpect(jsonPath("$.[*].reference").value(hasItem(DEFAULT_REFERENCE))) - .andExpect(jsonPath("$.[*].prefix").value(hasItem(DEFAULT_PREFIX))) - .andExpect(jsonPath("$.[*].name").value(hasItem(DEFAULT_NAME))) - .andExpect(jsonPath("$.[*].kind").value(hasItem(DEFAULT_KIND.toString()))) - .andExpect(jsonPath("$.[*].birthDate").value(hasItem(DEFAULT_BIRTH_DATE.toString()))) - .andExpect(jsonPath("$.[*].birthPlace").value(hasItem(DEFAULT_BIRTH_PLACE))) - .andExpect(jsonPath("$.[*].registrationCourt").value(hasItem(DEFAULT_REGISTRATION_COURT))) - .andExpect(jsonPath("$.[*].registrationNumber").value(hasItem(DEFAULT_REGISTRATION_NUMBER))) - .andExpect(jsonPath("$.[*].vatRegion").value(hasItem(DEFAULT_VAT_REGION.toString()))) - .andExpect(jsonPath("$.[*].vatNumber").value(hasItem(DEFAULT_VAT_NUMBER))) - .andExpect(jsonPath("$.[*].contractualSalutation").value(hasItem(DEFAULT_CONTRACTUAL_SALUTATION))) - .andExpect(jsonPath("$.[*].contractualAddress").value(hasItem(DEFAULT_CONTRACTUAL_ADDRESS))) - .andExpect(jsonPath("$.[*].billingSalutation").value(hasItem(DEFAULT_BILLING_SALUTATION))) - .andExpect(jsonPath("$.[*].billingAddress").value(hasItem(DEFAULT_BILLING_ADDRESS))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK))); - } - - @Test - @Transactional - public void getCustomer() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get the customer - restCustomerMockMvc.perform(get("/api/customers/{id}", customer.getId())) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.id").value(customer.getId().intValue())) - .andExpect(jsonPath("$.reference").value(DEFAULT_REFERENCE)) - .andExpect(jsonPath("$.prefix").value(DEFAULT_PREFIX)) - .andExpect(jsonPath("$.name").value(DEFAULT_NAME)) - .andExpect(jsonPath("$.kind").value(DEFAULT_KIND.toString())) - .andExpect(jsonPath("$.birthDate").value(DEFAULT_BIRTH_DATE.toString())) - .andExpect(jsonPath("$.birthPlace").value(DEFAULT_BIRTH_PLACE)) - .andExpect(jsonPath("$.registrationCourt").value(DEFAULT_REGISTRATION_COURT)) - .andExpect(jsonPath("$.registrationNumber").value(DEFAULT_REGISTRATION_NUMBER)) - .andExpect(jsonPath("$.vatRegion").value(DEFAULT_VAT_REGION.toString())) - .andExpect(jsonPath("$.vatNumber").value(DEFAULT_VAT_NUMBER)) - .andExpect(jsonPath("$.contractualSalutation").value(DEFAULT_CONTRACTUAL_SALUTATION)) - .andExpect(jsonPath("$.contractualAddress").value(DEFAULT_CONTRACTUAL_ADDRESS)) - .andExpect(jsonPath("$.billingSalutation").value(DEFAULT_BILLING_SALUTATION)) - .andExpect(jsonPath("$.billingAddress").value(DEFAULT_BILLING_ADDRESS)) - .andExpect(jsonPath("$.remark").value(DEFAULT_REMARK)); - } - - @Test - @Transactional - public void getAllCustomersByReferenceIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where reference equals to DEFAULT_REFERENCE - defaultCustomerShouldBeFound("reference.equals=" + DEFAULT_REFERENCE); - - // Get all the customerList where reference equals to UPDATED_REFERENCE - defaultCustomerShouldNotBeFound("reference.equals=" + UPDATED_REFERENCE); - } - - @Test - @Transactional - public void getAllCustomersByReferenceIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where reference in DEFAULT_REFERENCE or UPDATED_REFERENCE - defaultCustomerShouldBeFound("reference.in=" + DEFAULT_REFERENCE + "," + UPDATED_REFERENCE); - - // Get all the customerList where reference equals to UPDATED_REFERENCE - defaultCustomerShouldNotBeFound("reference.in=" + UPDATED_REFERENCE); - } - - @Test - @Transactional - public void getAllCustomersByReferenceIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where reference is not null - defaultCustomerShouldBeFound("reference.specified=true"); - - // Get all the customerList where reference is null - defaultCustomerShouldNotBeFound("reference.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByReferenceIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where reference greater than or equals to DEFAULT_REFERENCE - defaultCustomerShouldBeFound("reference.greaterOrEqualThan=" + DEFAULT_REFERENCE); - - // Get all the customerList where reference greater than or equals to (DEFAULT_REFERENCE + 1) - defaultCustomerShouldNotBeFound("reference.greaterOrEqualThan=" + (DEFAULT_REFERENCE + 1)); - } - - @Test - @Transactional - public void getAllCustomersByReferenceIsLessThanSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where reference less than or equals to DEFAULT_REFERENCE - defaultCustomerShouldNotBeFound("reference.lessThan=" + DEFAULT_REFERENCE); - - // Get all the customerList where reference less than or equals to (DEFAULT_REFERENCE + 1) - defaultCustomerShouldBeFound("reference.lessThan=" + (DEFAULT_REFERENCE + 1)); - } - - @Test - @Transactional - public void getAllCustomersByPrefixIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where prefix equals to DEFAULT_PREFIX - defaultCustomerShouldBeFound("prefix.equals=" + DEFAULT_PREFIX); - - // Get all the customerList where prefix equals to UPDATED_PREFIX - defaultCustomerShouldNotBeFound("prefix.equals=" + UPDATED_PREFIX); - } - - @Test - @Transactional - public void getAllCustomersByPrefixIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where prefix in DEFAULT_PREFIX or UPDATED_PREFIX - defaultCustomerShouldBeFound("prefix.in=" + DEFAULT_PREFIX + "," + UPDATED_PREFIX); - - // Get all the customerList where prefix equals to UPDATED_PREFIX - defaultCustomerShouldNotBeFound("prefix.in=" + UPDATED_PREFIX); - } - - @Test - @Transactional - public void getAllCustomersByPrefixIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where prefix is not null - defaultCustomerShouldBeFound("prefix.specified=true"); - - // Get all the customerList where prefix is null - defaultCustomerShouldNotBeFound("prefix.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByNameIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where name equals to DEFAULT_NAME - defaultCustomerShouldBeFound("name.equals=" + DEFAULT_NAME); - - // Get all the customerList where name equals to UPDATED_NAME - defaultCustomerShouldNotBeFound("name.equals=" + UPDATED_NAME); - } - - @Test - @Transactional - public void getAllCustomersByNameIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where name in DEFAULT_NAME or UPDATED_NAME - defaultCustomerShouldBeFound("name.in=" + DEFAULT_NAME + "," + UPDATED_NAME); - - // Get all the customerList where name equals to UPDATED_NAME - defaultCustomerShouldNotBeFound("name.in=" + UPDATED_NAME); - } - - @Test - @Transactional - public void getAllCustomersByNameIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where name is not null - defaultCustomerShouldBeFound("name.specified=true"); - - // Get all the customerList where name is null - defaultCustomerShouldNotBeFound("name.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByKindIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where kind equals to DEFAULT_KIND - defaultCustomerShouldBeFound("kind.equals=" + DEFAULT_KIND); - - // Get all the customerList where kind equals to UPDATED_KIND - defaultCustomerShouldNotBeFound("kind.equals=" + UPDATED_KIND); - } - - @Test - @Transactional - public void getAllCustomersByKindIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where kind in DEFAULT_KIND or UPDATED_KIND - defaultCustomerShouldBeFound("kind.in=" + DEFAULT_KIND + "," + UPDATED_KIND); - - // Get all the customerList where kind equals to UPDATED_KIND - defaultCustomerShouldNotBeFound("kind.in=" + UPDATED_KIND); - } - - @Test - @Transactional - public void getAllCustomersByKindIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where kind is not null - defaultCustomerShouldBeFound("kind.specified=true"); - - // Get all the customerList where kind is null - defaultCustomerShouldNotBeFound("kind.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByBirthDateIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where birthDate equals to DEFAULT_BIRTH_DATE - defaultCustomerShouldBeFound("birthDate.equals=" + DEFAULT_BIRTH_DATE); - - // Get all the customerList where birthDate equals to UPDATED_BIRTH_DATE - defaultCustomerShouldNotBeFound("birthDate.equals=" + UPDATED_BIRTH_DATE); - } - - @Test - @Transactional - public void getAllCustomersByBirthDateIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where birthDate in DEFAULT_BIRTH_DATE or UPDATED_BIRTH_DATE - defaultCustomerShouldBeFound("birthDate.in=" + DEFAULT_BIRTH_DATE + "," + UPDATED_BIRTH_DATE); - - // Get all the customerList where birthDate equals to UPDATED_BIRTH_DATE - defaultCustomerShouldNotBeFound("birthDate.in=" + UPDATED_BIRTH_DATE); - } - - @Test - @Transactional - public void getAllCustomersByBirthDateIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where birthDate is not null - defaultCustomerShouldBeFound("birthDate.specified=true"); - - // Get all the customerList where birthDate is null - defaultCustomerShouldNotBeFound("birthDate.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByBirthDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where birthDate greater than or equals to DEFAULT_BIRTH_DATE - defaultCustomerShouldBeFound("birthDate.greaterOrEqualThan=" + DEFAULT_BIRTH_DATE); - - // Get all the customerList where birthDate greater than or equals to UPDATED_BIRTH_DATE - defaultCustomerShouldNotBeFound("birthDate.greaterOrEqualThan=" + UPDATED_BIRTH_DATE); - } - - @Test - @Transactional - public void getAllCustomersByBirthDateIsLessThanSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where birthDate less than or equals to DEFAULT_BIRTH_DATE - defaultCustomerShouldNotBeFound("birthDate.lessThan=" + DEFAULT_BIRTH_DATE); - - // Get all the customerList where birthDate less than or equals to UPDATED_BIRTH_DATE - defaultCustomerShouldBeFound("birthDate.lessThan=" + UPDATED_BIRTH_DATE); - } - - @Test - @Transactional - public void getAllCustomersByBirthPlaceIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where birthPlace equals to DEFAULT_BIRTH_PLACE - defaultCustomerShouldBeFound("birthPlace.equals=" + DEFAULT_BIRTH_PLACE); - - // Get all the customerList where birthPlace equals to UPDATED_BIRTH_PLACE - defaultCustomerShouldNotBeFound("birthPlace.equals=" + UPDATED_BIRTH_PLACE); - } - - @Test - @Transactional - public void getAllCustomersByBirthPlaceIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where birthPlace in DEFAULT_BIRTH_PLACE or UPDATED_BIRTH_PLACE - defaultCustomerShouldBeFound("birthPlace.in=" + DEFAULT_BIRTH_PLACE + "," + UPDATED_BIRTH_PLACE); - - // Get all the customerList where birthPlace equals to UPDATED_BIRTH_PLACE - defaultCustomerShouldNotBeFound("birthPlace.in=" + UPDATED_BIRTH_PLACE); - } - - @Test - @Transactional - public void getAllCustomersByBirthPlaceIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where birthPlace is not null - defaultCustomerShouldBeFound("birthPlace.specified=true"); - - // Get all the customerList where birthPlace is null - defaultCustomerShouldNotBeFound("birthPlace.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByRegistrationCourtIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where registrationCourt equals to DEFAULT_REGISTRATION_COURT - defaultCustomerShouldBeFound("registrationCourt.equals=" + DEFAULT_REGISTRATION_COURT); - - // Get all the customerList where registrationCourt equals to UPDATED_REGISTRATION_COURT - defaultCustomerShouldNotBeFound("registrationCourt.equals=" + UPDATED_REGISTRATION_COURT); - } - - @Test - @Transactional - public void getAllCustomersByRegistrationCourtIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where registrationCourt in DEFAULT_REGISTRATION_COURT or UPDATED_REGISTRATION_COURT - defaultCustomerShouldBeFound("registrationCourt.in=" + DEFAULT_REGISTRATION_COURT + "," + UPDATED_REGISTRATION_COURT); - - // Get all the customerList where registrationCourt equals to UPDATED_REGISTRATION_COURT - defaultCustomerShouldNotBeFound("registrationCourt.in=" + UPDATED_REGISTRATION_COURT); - } - - @Test - @Transactional - public void getAllCustomersByRegistrationCourtIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where registrationCourt is not null - defaultCustomerShouldBeFound("registrationCourt.specified=true"); - - // Get all the customerList where registrationCourt is null - defaultCustomerShouldNotBeFound("registrationCourt.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByRegistrationNumberIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where registrationNumber equals to DEFAULT_REGISTRATION_NUMBER - defaultCustomerShouldBeFound("registrationNumber.equals=" + DEFAULT_REGISTRATION_NUMBER); - - // Get all the customerList where registrationNumber equals to UPDATED_REGISTRATION_NUMBER - defaultCustomerShouldNotBeFound("registrationNumber.equals=" + UPDATED_REGISTRATION_NUMBER); - } - - @Test - @Transactional - public void getAllCustomersByRegistrationNumberIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where registrationNumber in DEFAULT_REGISTRATION_NUMBER or UPDATED_REGISTRATION_NUMBER - defaultCustomerShouldBeFound( - "registrationNumber.in=" + DEFAULT_REGISTRATION_NUMBER + "," + UPDATED_REGISTRATION_NUMBER); - - // Get all the customerList where registrationNumber equals to UPDATED_REGISTRATION_NUMBER - defaultCustomerShouldNotBeFound("registrationNumber.in=" + UPDATED_REGISTRATION_NUMBER); - } - - @Test - @Transactional - public void getAllCustomersByRegistrationNumberIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where registrationNumber is not null - defaultCustomerShouldBeFound("registrationNumber.specified=true"); - - // Get all the customerList where registrationNumber is null - defaultCustomerShouldNotBeFound("registrationNumber.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByVatRegionIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where vatRegion equals to DEFAULT_VAT_REGION - defaultCustomerShouldBeFound("vatRegion.equals=" + DEFAULT_VAT_REGION); - - // Get all the customerList where vatRegion equals to UPDATED_VAT_REGION - defaultCustomerShouldNotBeFound("vatRegion.equals=" + UPDATED_VAT_REGION); - } - - @Test - @Transactional - public void getAllCustomersByVatRegionIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where vatRegion in DEFAULT_VAT_REGION or UPDATED_VAT_REGION - defaultCustomerShouldBeFound("vatRegion.in=" + DEFAULT_VAT_REGION + "," + UPDATED_VAT_REGION); - - // Get all the customerList where vatRegion equals to UPDATED_VAT_REGION - defaultCustomerShouldNotBeFound("vatRegion.in=" + UPDATED_VAT_REGION); - } - - @Test - @Transactional - public void getAllCustomersByVatRegionIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where vatRegion is not null - defaultCustomerShouldBeFound("vatRegion.specified=true"); - - // Get all the customerList where vatRegion is null - defaultCustomerShouldNotBeFound("vatRegion.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByVatNumberIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where vatNumber equals to DEFAULT_VAT_NUMBER - defaultCustomerShouldBeFound("vatNumber.equals=" + DEFAULT_VAT_NUMBER); - - // Get all the customerList where vatNumber equals to UPDATED_VAT_NUMBER - defaultCustomerShouldNotBeFound("vatNumber.equals=" + UPDATED_VAT_NUMBER); - } - - @Test - @Transactional - public void getAllCustomersByVatNumberIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where vatNumber in DEFAULT_VAT_NUMBER or UPDATED_VAT_NUMBER - defaultCustomerShouldBeFound("vatNumber.in=" + DEFAULT_VAT_NUMBER + "," + UPDATED_VAT_NUMBER); - - // Get all the customerList where vatNumber equals to UPDATED_VAT_NUMBER - defaultCustomerShouldNotBeFound("vatNumber.in=" + UPDATED_VAT_NUMBER); - } - - @Test - @Transactional - public void getAllCustomersByVatNumberIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where vatNumber is not null - defaultCustomerShouldBeFound("vatNumber.specified=true"); - - // Get all the customerList where vatNumber is null - defaultCustomerShouldNotBeFound("vatNumber.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByContractualSalutationIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where contractualSalutation equals to DEFAULT_CONTRACTUAL_SALUTATION - defaultCustomerShouldBeFound("contractualSalutation.equals=" + DEFAULT_CONTRACTUAL_SALUTATION); - - // Get all the customerList where contractualSalutation equals to UPDATED_CONTRACTUAL_SALUTATION - defaultCustomerShouldNotBeFound("contractualSalutation.equals=" + UPDATED_CONTRACTUAL_SALUTATION); - } - - @Test - @Transactional - public void getAllCustomersByContractualSalutationIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where contractualSalutation in DEFAULT_CONTRACTUAL_SALUTATION or - // UPDATED_CONTRACTUAL_SALUTATION - defaultCustomerShouldBeFound( - "contractualSalutation.in=" + DEFAULT_CONTRACTUAL_SALUTATION + "," + UPDATED_CONTRACTUAL_SALUTATION); - - // Get all the customerList where contractualSalutation equals to UPDATED_CONTRACTUAL_SALUTATION - defaultCustomerShouldNotBeFound("contractualSalutation.in=" + UPDATED_CONTRACTUAL_SALUTATION); - } - - @Test - @Transactional - public void getAllCustomersByContractualSalutationIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where contractualSalutation is not null - defaultCustomerShouldBeFound("contractualSalutation.specified=true"); - - // Get all the customerList where contractualSalutation is null - defaultCustomerShouldNotBeFound("contractualSalutation.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByContractualAddressIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where contractualAddress equals to DEFAULT_CONTRACTUAL_ADDRESS - defaultCustomerShouldBeFound("contractualAddress.equals=" + DEFAULT_CONTRACTUAL_ADDRESS); - - // Get all the customerList where contractualAddress equals to UPDATED_CONTRACTUAL_ADDRESS - defaultCustomerShouldNotBeFound("contractualAddress.equals=" + UPDATED_CONTRACTUAL_ADDRESS); - } - - @Test - @Transactional - public void getAllCustomersByContractualAddressIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where contractualAddress in DEFAULT_CONTRACTUAL_ADDRESS or UPDATED_CONTRACTUAL_ADDRESS - defaultCustomerShouldBeFound( - "contractualAddress.in=" + DEFAULT_CONTRACTUAL_ADDRESS + "," + UPDATED_CONTRACTUAL_ADDRESS); - - // Get all the customerList where contractualAddress equals to UPDATED_CONTRACTUAL_ADDRESS - defaultCustomerShouldNotBeFound("contractualAddress.in=" + UPDATED_CONTRACTUAL_ADDRESS); - } - - @Test - @Transactional - public void getAllCustomersByContractualAddressIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where contractualAddress is not null - defaultCustomerShouldBeFound("contractualAddress.specified=true"); - - // Get all the customerList where contractualAddress is null - defaultCustomerShouldNotBeFound("contractualAddress.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByBillingSalutationIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where billingSalutation equals to DEFAULT_BILLING_SALUTATION - defaultCustomerShouldBeFound("billingSalutation.equals=" + DEFAULT_BILLING_SALUTATION); - - // Get all the customerList where billingSalutation equals to UPDATED_BILLING_SALUTATION - defaultCustomerShouldNotBeFound("billingSalutation.equals=" + UPDATED_BILLING_SALUTATION); - } - - @Test - @Transactional - public void getAllCustomersByBillingSalutationIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where billingSalutation in DEFAULT_BILLING_SALUTATION or UPDATED_BILLING_SALUTATION - defaultCustomerShouldBeFound("billingSalutation.in=" + DEFAULT_BILLING_SALUTATION + "," + UPDATED_BILLING_SALUTATION); - - // Get all the customerList where billingSalutation equals to UPDATED_BILLING_SALUTATION - defaultCustomerShouldNotBeFound("billingSalutation.in=" + UPDATED_BILLING_SALUTATION); - } - - @Test - @Transactional - public void getAllCustomersByBillingSalutationIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where billingSalutation is not null - defaultCustomerShouldBeFound("billingSalutation.specified=true"); - - // Get all the customerList where billingSalutation is null - defaultCustomerShouldNotBeFound("billingSalutation.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByBillingAddressIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where billingAddress equals to DEFAULT_BILLING_ADDRESS - defaultCustomerShouldBeFound("billingAddress.equals=" + DEFAULT_BILLING_ADDRESS); - - // Get all the customerList where billingAddress equals to UPDATED_BILLING_ADDRESS - defaultCustomerShouldNotBeFound("billingAddress.equals=" + UPDATED_BILLING_ADDRESS); - } - - @Test - @Transactional - public void getAllCustomersByBillingAddressIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where billingAddress in DEFAULT_BILLING_ADDRESS or UPDATED_BILLING_ADDRESS - defaultCustomerShouldBeFound("billingAddress.in=" + DEFAULT_BILLING_ADDRESS + "," + UPDATED_BILLING_ADDRESS); - - // Get all the customerList where billingAddress equals to UPDATED_BILLING_ADDRESS - defaultCustomerShouldNotBeFound("billingAddress.in=" + UPDATED_BILLING_ADDRESS); - } - - @Test - @Transactional - public void getAllCustomersByBillingAddressIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where billingAddress is not null - defaultCustomerShouldBeFound("billingAddress.specified=true"); - - // Get all the customerList where billingAddress is null - defaultCustomerShouldNotBeFound("billingAddress.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByRemarkIsEqualToSomething() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where remark equals to DEFAULT_REMARK - defaultCustomerShouldBeFound("remark.equals=" + DEFAULT_REMARK); - - // Get all the customerList where remark equals to UPDATED_REMARK - defaultCustomerShouldNotBeFound("remark.equals=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllCustomersByRemarkIsInShouldWork() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where remark in DEFAULT_REMARK or UPDATED_REMARK - defaultCustomerShouldBeFound("remark.in=" + DEFAULT_REMARK + "," + UPDATED_REMARK); - - // Get all the customerList where remark equals to UPDATED_REMARK - defaultCustomerShouldNotBeFound("remark.in=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllCustomersByRemarkIsNullOrNotNull() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - // Get all the customerList where remark is not null - defaultCustomerShouldBeFound("remark.specified=true"); - - // Get all the customerList where remark is null - defaultCustomerShouldNotBeFound("remark.specified=false"); - } - - @Test - @Transactional - public void getAllCustomersByMembershipIsEqualToSomething() throws Exception { - // Initialize the database - Membership membership = MembershipResourceIntTest.createPersistentEntity(em, createPersistentEntity(em)); - em.persist(membership); - em.flush(); - customer.addMembership(membership); - customerRepository.saveAndFlush(customer); - Long membershipId = membership.getId(); - - // Get all the customerList where membership equals to membershipId - defaultCustomerShouldBeFound("membershipId.equals=" + membershipId); - - // Get all the customerList where membership equals to membershipId + 1 - defaultCustomerShouldNotBeFound("membershipId.equals=" + (membershipId + 1)); - } - - @Test - @Transactional - public void getAllCustomersBySepamandateIsEqualToSomething() throws Exception { - // Initialize the database - SepaMandate sepamandate = SepaMandateResourceIntTest.createEntity(em, createPersistentEntity(em)); - em.persist(sepamandate); - em.flush(); - customer.addSepamandate(sepamandate); - customerRepository.saveAndFlush(customer); - Long sepamandateId = sepamandate.getId(); - - // Get all the customerList where sepamandate equals to sepamandateId - defaultCustomerShouldBeFound("sepamandateId.equals=" + sepamandateId); - - // Get all the customerList where sepamandate equals to sepamandateId + 1 - defaultCustomerShouldNotBeFound("sepamandateId.equals=" + (sepamandateId + 1)); - } - - /** - * Executes the search, and checks that the default entity is returned - */ - private void defaultCustomerShouldBeFound(String filter) throws Exception { - restCustomerMockMvc.perform(get("/api/customers?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(customer.getId().intValue()))) - .andExpect(jsonPath("$.[*].reference").value(hasItem(DEFAULT_REFERENCE))) - .andExpect(jsonPath("$.[*].prefix").value(hasItem(DEFAULT_PREFIX))) - .andExpect(jsonPath("$.[*].name").value(hasItem(DEFAULT_NAME))) - .andExpect(jsonPath("$.[*].kind").value(hasItem(DEFAULT_KIND.toString()))) - .andExpect(jsonPath("$.[*].birthDate").value(hasItem(DEFAULT_BIRTH_DATE.toString()))) - .andExpect(jsonPath("$.[*].birthPlace").value(hasItem(DEFAULT_BIRTH_PLACE))) - .andExpect(jsonPath("$.[*].registrationCourt").value(hasItem(DEFAULT_REGISTRATION_COURT))) - .andExpect(jsonPath("$.[*].registrationNumber").value(hasItem(DEFAULT_REGISTRATION_NUMBER))) - .andExpect(jsonPath("$.[*].vatRegion").value(hasItem(DEFAULT_VAT_REGION.toString()))) - .andExpect(jsonPath("$.[*].vatNumber").value(hasItem(DEFAULT_VAT_NUMBER))) - .andExpect(jsonPath("$.[*].contractualSalutation").value(hasItem(DEFAULT_CONTRACTUAL_SALUTATION))) - .andExpect(jsonPath("$.[*].contractualAddress").value(hasItem(DEFAULT_CONTRACTUAL_ADDRESS))) - .andExpect(jsonPath("$.[*].billingSalutation").value(hasItem(DEFAULT_BILLING_SALUTATION))) - .andExpect(jsonPath("$.[*].billingAddress").value(hasItem(DEFAULT_BILLING_ADDRESS))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK))); - - // Check, that the count call also returns 1 - restCustomerMockMvc.perform(get("/api/customers/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("1")); - } - - /** - * Executes the search, and checks that the default entity is not returned - */ - private void defaultCustomerShouldNotBeFound(String filter) throws Exception { - restCustomerMockMvc.perform(get("/api/customers?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$").isArray()) - .andExpect(jsonPath("$").isEmpty()); - - // Check, that the count call also returns 0 - restCustomerMockMvc.perform(get("/api/customers/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("0")); - } - - @Test - @Transactional - public void getNonExistingCustomer() throws Exception { - // Get the customer - restCustomerMockMvc.perform(get("/api/customers/{id}", Long.MAX_VALUE)) - .andExpect(status().isNotFound()); - } - - @Test - @Transactional - public void updateCustomer() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - int databaseSizeBeforeUpdate = customerRepository.findAll().size(); - - // Update the customer - Customer updatedCustomer = customerRepository.findById(customer.getId()).get(); - // Disconnect from session so that the updates on updatedCustomer are not directly saved in db - em.detach(updatedCustomer); - updatedCustomer - .reference(null) - .prefix(null) - .name(UPDATED_NAME) - .kind(UPDATED_KIND) - .birthDate(UPDATED_BIRTH_DATE) - .birthPlace(UPDATED_BIRTH_PLACE) - .registrationCourt(UPDATED_REGISTRATION_COURT) - .registrationNumber(UPDATED_REGISTRATION_NUMBER) - .vatRegion(UPDATED_VAT_REGION) - .vatNumber(UPDATED_VAT_NUMBER) - .contractualSalutation(UPDATED_CONTRACTUAL_SALUTATION) - .contractualAddress(UPDATED_CONTRACTUAL_ADDRESS) - .billingSalutation(UPDATED_BILLING_SALUTATION) - .billingAddress(UPDATED_BILLING_ADDRESS) - .remark(UPDATED_REMARK); - CustomerDTO customerDTO = customerMapper.toDto(updatedCustomer); - - restCustomerMockMvc.perform( - put("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isOk()); - - // Validate the Customer in the database - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeUpdate); - Customer testCustomer = customerList.get(customerList.size() - 1); - assertThat(testCustomer.getReference()).isEqualTo(DEFAULT_REFERENCE); - assertThat(testCustomer.getPrefix()).isEqualTo(DEFAULT_PREFIX); - assertThat(testCustomer.getName()).isEqualTo(UPDATED_NAME); - assertThat(testCustomer.getKind()).isEqualTo(UPDATED_KIND); - assertThat(testCustomer.getBirthDate()).isEqualTo(UPDATED_BIRTH_DATE); - assertThat(testCustomer.getBirthPlace()).isEqualTo(UPDATED_BIRTH_PLACE); - assertThat(testCustomer.getRegistrationCourt()).isEqualTo(UPDATED_REGISTRATION_COURT); - assertThat(testCustomer.getRegistrationNumber()).isEqualTo(UPDATED_REGISTRATION_NUMBER); - assertThat(testCustomer.getVatRegion()).isEqualTo(UPDATED_VAT_REGION); - assertThat(testCustomer.getVatNumber()).isEqualTo(UPDATED_VAT_NUMBER); - assertThat(testCustomer.getContractualSalutation()).isEqualTo(UPDATED_CONTRACTUAL_SALUTATION); - assertThat(testCustomer.getContractualAddress()).isEqualTo(UPDATED_CONTRACTUAL_ADDRESS); - assertThat(testCustomer.getBillingSalutation()).isEqualTo(UPDATED_BILLING_SALUTATION); - assertThat(testCustomer.getBillingAddress()).isEqualTo(UPDATED_BILLING_ADDRESS); - assertThat(testCustomer.getRemark()).isEqualTo(UPDATED_REMARK); - } - - @Test - @Transactional - public void updateNonExistingCustomer() throws Exception { - int databaseSizeBeforeUpdate = customerRepository.findAll().size(); - - // Create the Customer - CustomerDTO customerDTO = customerMapper.toDto(customer); - - // If the entity doesn't have an ID, it will throw BadRequestAlertException - restCustomerMockMvc.perform( - put("/api/customers") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(customerDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Customer in the database - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeUpdate); - } - - @Test - @Transactional - public void deleteCustomer() throws Exception { - // Initialize the database - customerRepository.saveAndFlush(customer); - - int databaseSizeBeforeDelete = customerRepository.findAll().size(); - - // Delete the customer - restCustomerMockMvc.perform( - delete("/api/customers/{id}", customer.getId()) - .accept(TestUtil.APPLICATION_JSON_UTF8)) - .andExpect(status().isBadRequest()); - - // Validate the database is unchanged - List customerList = customerRepository.findAll(); - assertThat(customerList).hasSize(databaseSizeBeforeDelete); - } - - @Test - @Transactional - public void equalsVerifier() throws Exception { - TestUtil.equalsVerifier(Customer.class); - Customer customer1 = new Customer(); - customer1.setId(1L); - Customer customer2 = new Customer(); - customer2.setId(customer1.getId()); - assertThat(customer1).isEqualTo(customer2); - customer2.setId(2L); - assertThat(customer1).isNotEqualTo(customer2); - customer1.setId(null); - assertThat(customer1).isNotEqualTo(customer2); - } - - @Test - @Transactional - public void dtoEqualsVerifier() throws Exception { - TestUtil.equalsVerifier(CustomerDTO.class); - CustomerDTO customerDTO1 = new CustomerDTO(); - customerDTO1.setId(1L); - CustomerDTO customerDTO2 = new CustomerDTO(); - assertThat(customerDTO1).isNotEqualTo(customerDTO2); - customerDTO2.setId(customerDTO1.getId()); - assertThat(customerDTO1).isEqualTo(customerDTO2); - customerDTO2.setId(2L); - assertThat(customerDTO1).isNotEqualTo(customerDTO2); - customerDTO1.setId(null); - assertThat(customerDTO1).isNotEqualTo(customerDTO2); - } - - @Test - @Transactional - public void testEntityFromId() { - assertThat(customerMapper.fromId(42L).getId()).isEqualTo(42); - assertThat(customerMapper.fromId(null)).isNull(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/LogsResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/LogsResourceIntTest.java deleted file mode 100644 index c6a0093c..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/LogsResourceIntTest.java +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.web.rest.vm.LoggerVM; - -import ch.qos.logback.classic.AsyncAppender; -import ch.qos.logback.classic.LoggerContext; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.slf4j.LoggerFactory; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -/** - * Test class for the LogsResource REST controller. - * - * @see LogsResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class LogsResourceIntTest { - - private MockMvc restLogsMockMvc; - - @Before - public void setup() { - LogsResource logsResource = new LogsResource(); - this.restLogsMockMvc = MockMvcBuilders - .standaloneSetup(logsResource) - .build(); - } - - @Test - public void getAllLogs() throws Exception { - restLogsMockMvc.perform(get("/management/logs")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); - } - - @Test - public void changeLogs() throws Exception { - LoggerVM logger = new LoggerVM(); - logger.setLevel("INFO"); - logger.setName("ROOT"); - - restLogsMockMvc.perform( - put("/management/logs") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(logger))) - .andExpect(status().isNoContent()); - } - - @Test - public void testLogstashAppender() { - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - assertThat(context.getLogger("ROOT").getAppender("ASYNC_LOGSTASH")).isInstanceOf(AsyncAppender.class); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/MembershipResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/MembershipResourceIntTest.java deleted file mode 100644 index 7d757ce0..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/MembershipResourceIntTest.java +++ /dev/null @@ -1,837 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.hasItem; -import static org.hostsharing.hsadminng.web.rest.TestUtil.createFormattingConversionService; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Asset; -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.repository.MembershipRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.MembershipQueryService; -import org.hostsharing.hsadminng.service.MembershipService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.dto.MembershipDTO; -import org.hostsharing.hsadminng.service.mapper.MembershipMapper; -import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.Validator; - -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.List; - -import javax.persistence.EntityManager; - -/** - * Test class for the MembershipResource REST controller. - * - * @see MembershipResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class MembershipResourceIntTest { - - private static final LocalDate DEFAULT_DOCUMENT_DATE = LocalDate.now(ZoneId.systemDefault()); - private static final LocalDate UPDATED_DOCUMENT_DATE = DEFAULT_DOCUMENT_DATE.plusDays(1); - - private static final LocalDate DEFAULT_MEMBER_FROM_DATE = DEFAULT_DOCUMENT_DATE.plusDays(2); - private static final LocalDate UPDATED_MEMBER_FROM_DATE = UPDATED_DOCUMENT_DATE.plusDays(8); - - private static final LocalDate DEFAULT_MEMBER_UNTIL_DATE = DEFAULT_MEMBER_FROM_DATE.plusYears(1) - .withMonth(12) - .withDayOfMonth(31); - private static final LocalDate UPDATED_MEMBER_UNTIL_DATE = UPDATED_MEMBER_FROM_DATE.plusYears(7) - .withMonth(12) - .withDayOfMonth(31); - - private static final LocalDate DEFAULT_ADMISSION_DOCUMENT_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_ADMISSION_DOCUMENT_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final LocalDate DEFAULT_CANCELLATION_DOCUMENT_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_CANCELLATION_DOCUMENT_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final String DEFAULT_REMARK = "AAAAAAAAAA"; - private static final String UPDATED_REMARK = "BBBBBBBBBB"; - - @Autowired - private MembershipRepository membershipRepository; - - @Autowired - private MembershipMapper membershipMapper; - - @Autowired - private MembershipService membershipService; - - @Autowired - private MembershipQueryService membershipQueryService; - - @Autowired - private MappingJackson2HttpMessageConverter jacksonMessageConverter; - - @Autowired - private PageableHandlerMethodArgumentResolver pageableArgumentResolver; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - @Autowired - private EntityManager em; - - @Autowired - private Validator validator; - - @MockBean - private UserRoleAssignmentService userRoleAssignmentService; - - private SecurityContextMock securityContext; - - private MockMvc restMembershipMockMvc; - - private Membership membership; - - @Before - public void setup() { - securityContext = SecurityContextMock.usingMock(userRoleAssignmentService) - .havingAuthenticatedUser() - .withAuthority(AuthoritiesConstants.ADMIN); - - MockitoAnnotations.initMocks(this); - final MembershipResource membershipResource = new MembershipResource(membershipService, membershipQueryService); - this.restMembershipMockMvc = MockMvcBuilders.standaloneSetup(membershipResource) - .setCustomArgumentResolvers(pageableArgumentResolver) - .setControllerAdvice(exceptionTranslator) - .setConversionService(createFormattingConversionService()) - .setMessageConverters(jacksonMessageConverter) - .setValidator(validator) - .build(); - } - - /** - * Create an entity for this test. - * - * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static Membership createEntity(EntityManager em) { - Membership membership = new Membership() - .admissionDocumentDate(DEFAULT_ADMISSION_DOCUMENT_DATE) - .cancellationDocumentDate(DEFAULT_CANCELLATION_DOCUMENT_DATE) - .memberFromDate(DEFAULT_MEMBER_FROM_DATE) - .memberUntilDate(DEFAULT_MEMBER_UNTIL_DATE) - .remark(DEFAULT_REMARK); - // Add required entity - Customer customer = CustomerResourceIntTest.createEntity(em); - em.persist(customer); - em.flush(); - membership.setCustomer(customer); - return membership; - } - - /** - * Create an entity for tests for a specific customer. - *

- * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static Membership createPersistentEntity(EntityManager em, final Customer customer) { - Membership membership = new Membership() - .admissionDocumentDate(DEFAULT_ADMISSION_DOCUMENT_DATE) - .memberFromDate(DEFAULT_MEMBER_FROM_DATE) - .memberUntilDate(DEFAULT_MEMBER_UNTIL_DATE) - .remark(DEFAULT_REMARK); - // Add required entity - membership.setCustomer(customer); - em.persist(membership); - em.flush(); - return membership; - } - - @Before - public void initTest() { - membership = createEntity(em); - } - - @Test - @Transactional - public void createMembership() throws Exception { - int databaseSizeBeforeCreate = membershipRepository.findAll().size(); - - // Create the Membership - MembershipDTO membershipDTO = membershipMapper.toDto(membership); - membershipDTO.setCustomerPrefix(null); - membershipDTO.setCustomerDisplayLabel(null); - membershipDTO.setDisplayLabel(null); - restMembershipMockMvc.perform( - post("/api/memberships") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(membershipDTO))) - .andExpect(status().isCreated()); - - // Validate the Membership in the database - List membershipList = membershipRepository.findAll(); - assertThat(membershipList).hasSize(databaseSizeBeforeCreate + 1); - Membership testMembership = membershipList.get(membershipList.size() - 1); - assertThat(testMembership.getAdmissionDocumentDate()).isEqualTo(DEFAULT_ADMISSION_DOCUMENT_DATE); - assertThat(testMembership.getCancellationDocumentDate()).isEqualTo(DEFAULT_CANCELLATION_DOCUMENT_DATE); - assertThat(testMembership.getMemberFromDate()).isEqualTo(DEFAULT_MEMBER_FROM_DATE); - assertThat(testMembership.getMemberUntilDate()).isEqualTo(DEFAULT_MEMBER_UNTIL_DATE); - assertThat(testMembership.getRemark()).isEqualTo(DEFAULT_REMARK); - } - - @Test - @Transactional - public void createCustomerWithExistingIdIsRejected() throws Exception { - // Initialize the database - final long existingCustomerId = membershipRepository.saveAndFlush(membership).getId(); - int databaseSizeBeforeCreate = membershipRepository.findAll().size(); - - // Create the Customer with an existing ID - membership.setId(existingCustomerId); - MembershipDTO membershipDTO = membershipMapper.toDto(membership); - - // An entity with an existing ID cannot be created, so this API call must fail - restMembershipMockMvc.perform( - post("/api/memberships") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(membershipDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Customer in the database - List membershipList = membershipRepository.findAll(); - assertThat(membershipList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void createCustomerWithNonExistingIdIsRejected() throws Exception { - int databaseSizeBeforeCreate = membershipRepository.findAll().size(); - - // Create the Membership with an ID for which no entity exists - membership.setId(1L); - MembershipDTO membershipDTO = membershipMapper.toDto(membership); - - // An entity with an existing ID cannot be created, so this API call must fail - restMembershipMockMvc.perform( - post("/api/memberships") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(membershipDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Membership in the database - List membershipList = membershipRepository.findAll(); - assertThat(membershipList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void checkAdmissionDocumentDateIsRequired() throws Exception { - int databaseSizeBeforeTest = membershipRepository.findAll().size(); - // set the field null - membership.setAdmissionDocumentDate(null); - - // Create the Membership, which fails. - MembershipDTO membershipDTO = membershipMapper.toDto(membership); - - restMembershipMockMvc.perform( - post("/api/memberships") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(membershipDTO))) - .andExpect(status().isBadRequest()); - - List membershipList = membershipRepository.findAll(); - assertThat(membershipList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkMemberFromDateIsRequired() throws Exception { - int databaseSizeBeforeTest = membershipRepository.findAll().size(); - // set the field null - membership.setMemberFromDate(null); - - // Create the Membership, which fails. - MembershipDTO membershipDTO = membershipMapper.toDto(membership); - - restMembershipMockMvc.perform( - post("/api/memberships") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(membershipDTO))) - .andExpect(status().isBadRequest()); - - List membershipList = membershipRepository.findAll(); - assertThat(membershipList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void getAllMemberships() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList - restMembershipMockMvc.perform(get("/api/memberships?sort=id,desc")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(membership.getId().intValue()))) - .andExpect(jsonPath("$.[*].admissionDocumentDate").value(hasItem(DEFAULT_ADMISSION_DOCUMENT_DATE.toString()))) - .andExpect( - jsonPath("$.[*].cancellationDocumentDate") - .value(hasItem(DEFAULT_CANCELLATION_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].memberFromDate").value(hasItem(DEFAULT_MEMBER_FROM_DATE.toString()))) - .andExpect(jsonPath("$.[*].memberUntilDate").value(hasItem(DEFAULT_MEMBER_UNTIL_DATE.toString()))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK))); - } - - @Test - @Transactional - public void getMembership() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get the membership - restMembershipMockMvc.perform(get("/api/memberships/{id}", membership.getId())) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.id").value(membership.getId().intValue())) - .andExpect(jsonPath("$.admissionDocumentDate").value(DEFAULT_ADMISSION_DOCUMENT_DATE.toString())) - .andExpect(jsonPath("$.cancellationDocumentDate").value(DEFAULT_CANCELLATION_DOCUMENT_DATE.toString())) - .andExpect(jsonPath("$.memberFromDate").value(DEFAULT_MEMBER_FROM_DATE.toString())) - .andExpect(jsonPath("$.memberUntilDate").value(DEFAULT_MEMBER_UNTIL_DATE.toString())) - .andExpect(jsonPath("$.remark").value(DEFAULT_REMARK)); - } - - @Test - @Transactional - public void getAllMembershipsByAdmissionDocumentDateIsEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where admissionDocumentDate equals to DEFAULT_ADMISSION_DOCUMENT_DATE - defaultMembershipShouldBeFound("admissionDocumentDate.equals=" + DEFAULT_ADMISSION_DOCUMENT_DATE); - - // Get all the membershipList where admissionDocumentDate equals to UPDATED_ADMISSION_DOCUMENT_DATE - defaultMembershipShouldNotBeFound("admissionDocumentDate.equals=" + UPDATED_ADMISSION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByAdmissionDocumentDateIsInShouldWork() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where admissionDocumentDate in DEFAULT_ADMISSION_DOCUMENT_DATE or - // UPDATED_ADMISSION_DOCUMENT_DATE - defaultMembershipShouldBeFound( - "admissionDocumentDate.in=" + DEFAULT_ADMISSION_DOCUMENT_DATE + "," + UPDATED_ADMISSION_DOCUMENT_DATE); - - // Get all the membershipList where admissionDocumentDate equals to UPDATED_ADMISSION_DOCUMENT_DATE - defaultMembershipShouldNotBeFound("admissionDocumentDate.in=" + UPDATED_ADMISSION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByAdmissionDocumentDateIsNullOrNotNull() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where admissionDocumentDate is not null - defaultMembershipShouldBeFound("admissionDocumentDate.specified=true"); - - // Get all the membershipList where admissionDocumentDate is null - defaultMembershipShouldNotBeFound("admissionDocumentDate.specified=false"); - } - - @Test - @Transactional - public void getAllMembershipsByAdmissionDocumentDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where admissionDocumentDate greater than or equals to DEFAULT_ADMISSION_DOCUMENT_DATE - defaultMembershipShouldBeFound("admissionDocumentDate.greaterOrEqualThan=" + DEFAULT_ADMISSION_DOCUMENT_DATE); - - // Get all the membershipList where admissionDocumentDate greater than or equals to UPDATED_ADMISSION_DOCUMENT_DATE - defaultMembershipShouldNotBeFound("admissionDocumentDate.greaterOrEqualThan=" + UPDATED_ADMISSION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByAdmissionDocumentDateIsLessThanSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where admissionDocumentDate less than or equals to DEFAULT_ADMISSION_DOCUMENT_DATE - defaultMembershipShouldNotBeFound("admissionDocumentDate.lessThan=" + DEFAULT_ADMISSION_DOCUMENT_DATE); - - // Get all the membershipList where admissionDocumentDate less than or equals to UPDATED_ADMISSION_DOCUMENT_DATE - defaultMembershipShouldBeFound("admissionDocumentDate.lessThan=" + UPDATED_ADMISSION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByCancellationDocumentDateIsEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where cancellationDocumentDate equals to DEFAULT_CANCELLATION_DOCUMENT_DATE - defaultMembershipShouldBeFound("cancellationDocumentDate.equals=" + DEFAULT_CANCELLATION_DOCUMENT_DATE); - - // Get all the membershipList where cancellationDocumentDate equals to UPDATED_CANCELLATION_DOCUMENT_DATE - defaultMembershipShouldNotBeFound("cancellationDocumentDate.equals=" + UPDATED_CANCELLATION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByCancellationDocumentDateIsInShouldWork() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where cancellationDocumentDate in DEFAULT_CANCELLATION_DOCUMENT_DATE or - // UPDATED_CANCELLATION_DOCUMENT_DATE - defaultMembershipShouldBeFound( - "cancellationDocumentDate.in=" + DEFAULT_CANCELLATION_DOCUMENT_DATE + "," + UPDATED_CANCELLATION_DOCUMENT_DATE); - - // Get all the membershipList where cancellationDocumentDate equals to UPDATED_CANCELLATION_DOCUMENT_DATE - defaultMembershipShouldNotBeFound("cancellationDocumentDate.in=" + UPDATED_CANCELLATION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByCancellationDocumentDateIsNullOrNotNull() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where cancellationDocumentDate is not null - defaultMembershipShouldBeFound("cancellationDocumentDate.specified=true"); - - // Get all the membershipList where cancellationDocumentDate is null - defaultMembershipShouldNotBeFound("cancellationDocumentDate.specified=false"); - } - - @Test - @Transactional - public void getAllMembershipsByCancellationDocumentDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where cancellationDocumentDate greater than or equals to - // DEFAULT_CANCELLATION_DOCUMENT_DATE - defaultMembershipShouldBeFound("cancellationDocumentDate.greaterOrEqualThan=" + DEFAULT_CANCELLATION_DOCUMENT_DATE); - - // Get all the membershipList where cancellationDocumentDate greater than or equals to - // UPDATED_CANCELLATION_DOCUMENT_DATE - defaultMembershipShouldNotBeFound("cancellationDocumentDate.greaterOrEqualThan=" + UPDATED_CANCELLATION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByCancellationDocumentDateIsLessThanSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where cancellationDocumentDate less than or equals to DEFAULT_CANCELLATION_DOCUMENT_DATE - defaultMembershipShouldNotBeFound("cancellationDocumentDate.lessThan=" + DEFAULT_CANCELLATION_DOCUMENT_DATE); - - // Get all the membershipList where cancellationDocumentDate less than or equals to UPDATED_CANCELLATION_DOCUMENT_DATE - defaultMembershipShouldBeFound("cancellationDocumentDate.lessThan=" + UPDATED_CANCELLATION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByMemberFromDateIsEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberFromDate equals to DEFAULT_MEMBER_FROM_DATE - defaultMembershipShouldBeFound("memberFromDate.equals=" + DEFAULT_MEMBER_FROM_DATE); - - // Get all the membershipList where memberFromDate equals to UPDATED_MEMBER_FROM_DATE - defaultMembershipShouldNotBeFound("memberFromDate.equals=" + UPDATED_MEMBER_FROM_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByMemberFromDateIsInShouldWork() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberFromDate in DEFAULT_MEMBER_FROM_DATE or UPDATED_MEMBER_FROM_DATE - defaultMembershipShouldBeFound("memberFromDate.in=" + DEFAULT_MEMBER_FROM_DATE + "," + UPDATED_MEMBER_FROM_DATE); - - // Get all the membershipList where memberFromDate equals to UPDATED_MEMBER_FROM_DATE - defaultMembershipShouldNotBeFound("memberFromDate.in=" + UPDATED_MEMBER_FROM_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByMemberFromDateIsNullOrNotNull() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberFromDate is not null - defaultMembershipShouldBeFound("memberFromDate.specified=true"); - - // Get all the membershipList where memberFromDate is null - defaultMembershipShouldNotBeFound("memberFromDate.specified=false"); - } - - @Test - @Transactional - public void getAllMembershipsByMemberFromDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberFromDate greater than or equals to DEFAULT_MEMBER_FROM_DATE - defaultMembershipShouldBeFound("memberFromDate.greaterOrEqualThan=" + DEFAULT_MEMBER_FROM_DATE); - - // Get all the membershipList where memberFromDate greater than or equals to UPDATED_MEMBER_FROM_DATE - defaultMembershipShouldNotBeFound("memberFromDate.greaterOrEqualThan=" + UPDATED_MEMBER_FROM_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByMemberFromDateIsLessThanSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberFromDate less than or equals to DEFAULT_MEMBER_FROM_DATE - defaultMembershipShouldNotBeFound("memberFromDate.lessThan=" + DEFAULT_MEMBER_FROM_DATE); - - // Get all the membershipList where memberFromDate less than or equals to UPDATED_MEMBER_FROM_DATE - defaultMembershipShouldBeFound("memberFromDate.lessThan=" + UPDATED_MEMBER_FROM_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByMemberUntilDateIsEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberUntilDate equals to DEFAULT_MEMBER_UNTIL_DATE - defaultMembershipShouldBeFound("memberUntilDate.equals=" + DEFAULT_MEMBER_UNTIL_DATE); - - // Get all the membershipList where memberUntilDate equals to UPDATED_MEMBER_UNTIL_DATE - defaultMembershipShouldNotBeFound("memberUntilDate.equals=" + UPDATED_MEMBER_UNTIL_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByMemberUntilDateIsInShouldWork() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberUntilDate in DEFAULT_MEMBER_UNTIL_DATE or UPDATED_MEMBER_UNTIL_DATE - defaultMembershipShouldBeFound("memberUntilDate.in=" + DEFAULT_MEMBER_UNTIL_DATE + "," + UPDATED_MEMBER_UNTIL_DATE); - - // Get all the membershipList where memberUntilDate equals to UPDATED_MEMBER_UNTIL_DATE - defaultMembershipShouldNotBeFound("memberUntilDate.in=" + UPDATED_MEMBER_UNTIL_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByMemberUntilDateIsNullOrNotNull() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberUntilDate is not null - defaultMembershipShouldBeFound("memberUntilDate.specified=true"); - - // Get all the membershipList where memberUntilDate is null - defaultMembershipShouldNotBeFound("memberUntilDate.specified=false"); - } - - @Test - @Transactional - public void getAllMembershipsByMemberUntilDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberUntilDate greater than or equals to DEFAULT_MEMBER_UNTIL_DATE - defaultMembershipShouldBeFound("memberUntilDate.greaterOrEqualThan=" + DEFAULT_MEMBER_UNTIL_DATE); - - // Get all the membershipList where memberUntilDate greater than or equals to UPDATED_MEMBER_UNTIL_DATE - defaultMembershipShouldNotBeFound("memberUntilDate.greaterOrEqualThan=" + UPDATED_MEMBER_UNTIL_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByMemberUntilDateIsLessThanSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where memberUntilDate less than or equals to DEFAULT_MEMBER_UNTIL_DATE - defaultMembershipShouldNotBeFound("memberUntilDate.lessThan=" + DEFAULT_MEMBER_UNTIL_DATE); - - // Get all the membershipList where memberUntilDate less than or equals to UPDATED_MEMBER_UNTIL_DATE - defaultMembershipShouldBeFound("memberUntilDate.lessThan=" + UPDATED_MEMBER_UNTIL_DATE); - } - - @Test - @Transactional - public void getAllMembershipsByRemarkIsEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where remark equals to DEFAULT_REMARK - defaultMembershipShouldBeFound("remark.equals=" + DEFAULT_REMARK); - - // Get all the membershipList where remark equals to UPDATED_REMARK - defaultMembershipShouldNotBeFound("remark.equals=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllMembershipsByRemarkIsInShouldWork() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where remark in DEFAULT_REMARK or UPDATED_REMARK - defaultMembershipShouldBeFound("remark.in=" + DEFAULT_REMARK + "," + UPDATED_REMARK); - - // Get all the membershipList where remark equals to UPDATED_REMARK - defaultMembershipShouldNotBeFound("remark.in=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllMembershipsByRemarkIsNullOrNotNull() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - // Get all the membershipList where remark is not null - defaultMembershipShouldBeFound("remark.specified=true"); - - // Get all the membershipList where remark is null - defaultMembershipShouldNotBeFound("remark.specified=false"); - } - - @Test - @Transactional - public void getAllMembershipsByShareIsEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - Share share = ShareResourceIntTest.createPersistentEntity(em, membership); - - Long shareId = share.getId(); - - // Get all the membershipList where share equals to shareId - defaultMembershipShouldBeFound("shareId.equals=" + shareId); - - // Get all the membershipList where share equals to shareId + 1 - defaultMembershipShouldNotBeFound("shareId.equals=" + (shareId + 1)); - } - - @Test - @Transactional - public void getAllMembershipsByAssetIsEqualToSomething() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - Asset asset = AssetResourceIntTest.createPersistentEntity(em, membership); - - Long assetId = asset.getId(); - - // Get all the membershipList where asset equals to assetId - defaultMembershipShouldBeFound("assetId.equals=" + assetId); - - // Get all the membershipList where asset equals to assetId + 1 - defaultMembershipShouldNotBeFound("assetId.equals=" + (assetId + 1)); - } - - @Test - @Transactional - public void getAllMembershipsByCustomerIsEqualToSomething() throws Exception { - // Initialize the database - Customer customer = CustomerResourceIntTest.createPersistentEntity(em); - membership.setCustomer(customer); - membershipRepository.saveAndFlush(membership); - Long customerId = customer.getId(); - - // Get all the membershipList where customer equals to customerId - defaultMembershipShouldBeFound("customerId.equals=" + customerId); - - // Get all the membershipList where customer equals to customerId + 1 - defaultMembershipShouldNotBeFound("customerId.equals=" + (customerId + 1)); - } - - /** - * Executes the search, and checks that the default entity is returned - */ - private void defaultMembershipShouldBeFound(String filter) throws Exception { - restMembershipMockMvc.perform(get("/api/memberships?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(membership.getId().intValue()))) - .andExpect(jsonPath("$.[*].admissionDocumentDate").value(hasItem(DEFAULT_ADMISSION_DOCUMENT_DATE.toString()))) - .andExpect( - jsonPath("$.[*].cancellationDocumentDate") - .value(hasItem(DEFAULT_CANCELLATION_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].memberFromDate").value(hasItem(DEFAULT_MEMBER_FROM_DATE.toString()))) - .andExpect(jsonPath("$.[*].memberUntilDate").value(hasItem(DEFAULT_MEMBER_UNTIL_DATE.toString()))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK))); - - // Check, that the count call also returns 1 - restMembershipMockMvc.perform(get("/api/memberships/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("1")); - } - - /** - * Executes the search, and checks that the default entity is not returned - */ - private void defaultMembershipShouldNotBeFound(String filter) throws Exception { - restMembershipMockMvc.perform(get("/api/memberships?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$").isArray()) - .andExpect(jsonPath("$").isEmpty()); - - // Check, that the count call also returns 0 - restMembershipMockMvc.perform(get("/api/memberships/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("0")); - } - - @Test - @Transactional - public void getNonExistingMembership() throws Exception { - // Get the membership - restMembershipMockMvc.perform(get("/api/memberships/{id}", Long.MAX_VALUE)) - .andExpect(status().isNotFound()); - } - - @Test - @Transactional - public void updateMembership() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - int databaseSizeBeforeUpdate = membershipRepository.findAll().size(); - - // Update the membership - Membership updatedMembership = membershipRepository.findById(membership.getId()).get(); - // Disconnect from session so that the updates on updatedMembership are not directly saved in db - em.detach(updatedMembership); - updatedMembership - .cancellationDocumentDate(UPDATED_CANCELLATION_DOCUMENT_DATE) - .memberUntilDate(UPDATED_MEMBER_UNTIL_DATE) - .remark(UPDATED_REMARK); - MembershipDTO membershipDTO = membershipMapper.toDto(updatedMembership); - - restMembershipMockMvc.perform( - put("/api/memberships") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(membershipDTO))) - .andExpect(status().isOk()); - - // Validate the Membership in the database - List membershipList = membershipRepository.findAll(); - assertThat(membershipList).hasSize(databaseSizeBeforeUpdate); - Membership testMembership = membershipList.get(membershipList.size() - 1); - assertThat(testMembership.getAdmissionDocumentDate()).isEqualTo(DEFAULT_ADMISSION_DOCUMENT_DATE); - assertThat(testMembership.getCancellationDocumentDate()).isEqualTo(UPDATED_CANCELLATION_DOCUMENT_DATE); - assertThat(testMembership.getMemberFromDate()).isEqualTo(DEFAULT_MEMBER_FROM_DATE); - assertThat(testMembership.getMemberUntilDate()).isEqualTo(UPDATED_MEMBER_UNTIL_DATE); - assertThat(testMembership.getRemark()).isEqualTo(UPDATED_REMARK); - } - - @Test - @Transactional - public void updateNonExistingMembership() throws Exception { - int databaseSizeBeforeUpdate = membershipRepository.findAll().size(); - - // Create the Membership - MembershipDTO membershipDTO = membershipMapper.toDto(membership); - - // If the entity doesn't have an ID, it will throw BadRequestAlertException - restMembershipMockMvc.perform( - put("/api/memberships") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(membershipDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Membership in the database - List membershipList = membershipRepository.findAll(); - assertThat(membershipList).hasSize(databaseSizeBeforeUpdate); - } - - @Test - @Transactional - public void deleteMembership() throws Exception { - // Initialize the database - membershipRepository.saveAndFlush(membership); - - int databaseSizeBeforeDelete = membershipRepository.findAll().size(); - - // Delete the membership - restMembershipMockMvc.perform( - delete("/api/memberships/{id}", membership.getId()) - .accept(TestUtil.APPLICATION_JSON_UTF8)) - .andExpect(status().isBadRequest()); - - // Validate the database is unchanged - List membershipList = membershipRepository.findAll(); - assertThat(membershipList).hasSize(databaseSizeBeforeDelete); - } - - @Test - @Transactional - public void equalsVerifier() throws Exception { - TestUtil.equalsVerifier(Membership.class); - Membership membership1 = new Membership(); - membership1.setId(1L); - Membership membership2 = new Membership(); - membership2.setId(membership1.getId()); - assertThat(membership1).isEqualTo(membership2); - membership2.setId(2L); - assertThat(membership1).isNotEqualTo(membership2); - membership1.setId(null); - assertThat(membership1).isNotEqualTo(membership2); - } - - @Test - @Transactional - public void dtoEqualsVerifier() throws Exception { - TestUtil.equalsVerifier(MembershipDTO.class); - MembershipDTO membershipDTO1 = new MembershipDTO(); - membershipDTO1.setId(1L); - MembershipDTO membershipDTO2 = new MembershipDTO(); - assertThat(membershipDTO1).isNotEqualTo(membershipDTO2); - membershipDTO2.setId(membershipDTO1.getId()); - assertThat(membershipDTO1).isEqualTo(membershipDTO2); - membershipDTO2.setId(2L); - assertThat(membershipDTO1).isNotEqualTo(membershipDTO2); - membershipDTO1.setId(null); - assertThat(membershipDTO1).isNotEqualTo(membershipDTO2); - } - - @Test - @Transactional - public void testEntityFromId() { - assertThat(membershipMapper.fromId(42L).getId()).isEqualTo(42); - assertThat(membershipMapper.fromId(null)).isNull(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/MembershipResourceUnitTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/MembershipResourceUnitTest.java deleted file mode 100644 index 89db87df..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/MembershipResourceUnitTest.java +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.ThrowableAssert.catchThrowable; - -import org.hostsharing.hsadminng.service.dto.MembershipDTO; -import org.hostsharing.hsadminng.service.dto.MembershipDTOUnitTest; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -// Currently this class tests mostly special 'bad paths' -// which make little sense to test in *ResourceIntTest. -public class MembershipResourceUnitTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @InjectMocks - private MembershipResource membershipResource; - - @Test - public void createSepaMandateWithoutIdThrowsBadRequestException() { - - // given - final MembershipDTO givenDto = MembershipDTOUnitTest.createRandomDTO(null, 1L); - - // when - final Throwable actual = catchThrowable(() -> membershipResource.updateMembership(givenDto)); - - // then - assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> { - assertThat(bre.getErrorKey()).isEqualTo("idnull"); - assertThat(bre.getParam()).isEqualTo("membership"); - }); - } - - @Test - public void createSepaMandateWithIdThrowsBadRequestException() { - - // given - final MembershipDTO givenDto = MembershipDTOUnitTest.createRandomDTO(2L, 1L); - - // when - final Throwable actual = catchThrowable(() -> membershipResource.createMembership(givenDto)); - - // then - assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> { - assertThat(bre.getErrorKey()).isEqualTo("idexists"); - assertThat(bre.getParam()).isEqualTo("membership"); - }); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/SepaMandateResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/SepaMandateResourceIntTest.java deleted file mode 100644 index 565b058d..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/SepaMandateResourceIntTest.java +++ /dev/null @@ -1,1009 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.hasItem; -import static org.hostsharing.hsadminng.web.rest.TestUtil.createFormattingConversionService; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Customer; -import org.hostsharing.hsadminng.domain.SepaMandate; -import org.hostsharing.hsadminng.repository.SepaMandateRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.SepaMandateQueryService; -import org.hostsharing.hsadminng.service.SepaMandateService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.dto.SepaMandateDTO; -import org.hostsharing.hsadminng.service.mapper.SepaMandateMapper; -import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.Validator; - -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.List; - -import javax.persistence.EntityManager; - -/** - * Test class for the SepaMandateResource REST controller. - * - * @see SepaMandateResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class SepaMandateResourceIntTest { - - private static final String DEFAULT_REFERENCE = "AAAAAAAAAA"; - private static final String UPDATED_REFERENCE = "BBBBBBBBBB"; - - private static final String DEFAULT_IBAN = "AAAAAAAAAA"; - private static final String UPDATED_IBAN = "BBBBBBBBBB"; - - private static final String DEFAULT_BIC = "AAAAAAAAAA"; - private static final String UPDATED_BIC = "BBBBBBBBBB"; - - private static final LocalDate DEFAULT_VALID_FROM_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_VALID_FROM_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final LocalDate DEFAULT_VALID_UNTIL_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_VALID_UNTIL_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final LocalDate DEFAULT_GRANTING_DOCUMENT_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_GRANTING_DOCUMENT_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final LocalDate DEFAULT_REVOKATION_DOCUMENT_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_REVOKATION_DOCUMENT_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final LocalDate DEFAULT_LAST_USED_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_LAST_USED_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final String DEFAULT_REMARK = "AAAAAAAAAA"; - private static final String UPDATED_REMARK = "BBBBBBBBBB"; - - @Autowired - private SepaMandateRepository sepaMandateRepository; - - @Autowired - private SepaMandateMapper sepaMandateMapper; - - @Autowired - private SepaMandateService sepaMandateService; - - @Autowired - private SepaMandateQueryService sepaMandateQueryService; - - @Autowired - private MappingJackson2HttpMessageConverter jacksonMessageConverter; - - @Autowired - private PageableHandlerMethodArgumentResolver pageableArgumentResolver; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - @Autowired - private EntityManager em; - - @Autowired - private Validator validator; - - @MockBean - private UserRoleAssignmentService userRoleAssignmentService; - - private MockMvc restSepaMandateMockMvc; - - private SepaMandate sepaMandate; - - @Before - public void setup() { - SecurityContextMock.usingMock(userRoleAssignmentService) - .havingAuthenticatedUser() - .withAuthority(AuthoritiesConstants.ADMIN); - - MockitoAnnotations.initMocks(this); - final SepaMandateResource sepaMandateResource = new SepaMandateResource(sepaMandateService, sepaMandateQueryService); - this.restSepaMandateMockMvc = MockMvcBuilders.standaloneSetup(sepaMandateResource) - .setCustomArgumentResolvers(pageableArgumentResolver) - .setControllerAdvice(exceptionTranslator) - .setConversionService(createFormattingConversionService()) - .setMessageConverters(jacksonMessageConverter) - .setValidator(validator) - .build(); - } - - /** - * Create an entity for this test. - *

- * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static SepaMandate createEntity(EntityManager em) { - SepaMandate sepaMandate = new SepaMandate() - .reference(DEFAULT_REFERENCE) - .iban(DEFAULT_IBAN) - .bic(DEFAULT_BIC) - .grantingDocumentDate(DEFAULT_GRANTING_DOCUMENT_DATE) - .revokationDocumentDate(DEFAULT_REVOKATION_DOCUMENT_DATE) - .validFromDate(DEFAULT_VALID_FROM_DATE) - .validUntilDate(DEFAULT_VALID_UNTIL_DATE) - .lastUsedDate(DEFAULT_LAST_USED_DATE) - .remark(DEFAULT_REMARK); - // Add required entity - Customer customer = CustomerResourceIntTest.createEntity(em); - em.persist(customer); - em.flush(); - sepaMandate.setCustomer(customer); - return sepaMandate; - } - - /** - * Create an entity for tests with a specific customer. - *

- * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static SepaMandate createEntity(EntityManager em, final Customer customer) { - SepaMandate sepaMandate = new SepaMandate() - .reference(DEFAULT_REFERENCE) - .iban(DEFAULT_IBAN) - .bic(DEFAULT_BIC) - .grantingDocumentDate(DEFAULT_GRANTING_DOCUMENT_DATE) - .validFromDate(DEFAULT_VALID_FROM_DATE) - .validUntilDate(DEFAULT_VALID_UNTIL_DATE) - .lastUsedDate(DEFAULT_LAST_USED_DATE) - .revokationDocumentDate(DEFAULT_REVOKATION_DOCUMENT_DATE) - .remark(DEFAULT_REMARK); - // Add required entity - sepaMandate.setCustomer(customer); - return sepaMandate; - } - - @Before - public void initTest() { - sepaMandate = createEntity(em); - } - - @Test - @Transactional - public void createSepaMandate() throws Exception { - int databaseSizeBeforeCreate = sepaMandateRepository.findAll().size(); - - // Create the SepaMandate - SepaMandateDTO sepaMandateDTO = sepaMandateMapper.toDto(sepaMandate); - sepaMandateDTO.setCustomerDisplayLabel(null); - sepaMandateDTO.setRemark(null); - sepaMandateDTO.setRevokationDocumentDate(null); - sepaMandateDTO.setLastUsedDate(null); - - restSepaMandateMockMvc.perform( - post("/api/sepa-mandates") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(sepaMandateDTO))) - .andExpect(status().isCreated()); - - // Validate the SepaMandate in the database - List sepaMandateList = sepaMandateRepository.findAll(); - assertThat(sepaMandateList).hasSize(databaseSizeBeforeCreate + 1); - SepaMandate testSepaMandate = sepaMandateList.get(sepaMandateList.size() - 1); - assertThat(testSepaMandate.getReference()).isEqualTo(DEFAULT_REFERENCE); - assertThat(testSepaMandate.getIban()).isEqualTo(DEFAULT_IBAN); - assertThat(testSepaMandate.getBic()).isEqualTo(DEFAULT_BIC); - assertThat(testSepaMandate.getGrantingDocumentDate()).isEqualTo(DEFAULT_GRANTING_DOCUMENT_DATE); - assertThat(testSepaMandate.getRevokationDocumentDate()).isNull(); - assertThat(testSepaMandate.getValidFromDate()).isEqualTo(DEFAULT_VALID_FROM_DATE); - assertThat(testSepaMandate.getValidUntilDate()).isEqualTo(DEFAULT_VALID_UNTIL_DATE); - assertThat(testSepaMandate.getLastUsedDate()).isNull(); - assertThat(testSepaMandate.getRemark()).isNull(); - } - - @Test - @Transactional - public void createSepaMandateWithExistingId() throws Exception { - int databaseSizeBeforeCreate = sepaMandateRepository.findAll().size(); - - // Create the SepaMandate with an existing ID - sepaMandate.setId(1L); - SepaMandateDTO sepaMandateDTO = sepaMandateMapper.toDto(sepaMandate); - - // An entity with an existing ID cannot be created, so this API call must fail - restSepaMandateMockMvc.perform( - post("/api/sepa-mandates") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(sepaMandateDTO))) - .andExpect(status().isBadRequest()); - - // Validate the SepaMandate in the database - List sepaMandateList = sepaMandateRepository.findAll(); - assertThat(sepaMandateList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void checkReferenceIsRequired() throws Exception { - int databaseSizeBeforeTest = sepaMandateRepository.findAll().size(); - // set the field null - sepaMandate.setReference(null); - - // Create the SepaMandate, which fails. - SepaMandateDTO sepaMandateDTO = sepaMandateMapper.toDto(sepaMandate); - - restSepaMandateMockMvc.perform( - post("/api/sepa-mandates") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(sepaMandateDTO))) - .andExpect(status().isBadRequest()); - - List sepaMandateList = sepaMandateRepository.findAll(); - assertThat(sepaMandateList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkGrantingDocumentDateIsRequired() throws Exception { - int databaseSizeBeforeTest = sepaMandateRepository.findAll().size(); - // set the field null - sepaMandate.setGrantingDocumentDate(null); - - // Create the SepaMandate, which fails. - SepaMandateDTO sepaMandateDTO = sepaMandateMapper.toDto(sepaMandate); - - restSepaMandateMockMvc.perform( - post("/api/sepa-mandates") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(sepaMandateDTO))) - .andExpect(status().isBadRequest()); - - List sepaMandateList = sepaMandateRepository.findAll(); - assertThat(sepaMandateList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkValidFromDateIsRequired() throws Exception { - int databaseSizeBeforeTest = sepaMandateRepository.findAll().size(); - // set the field null - sepaMandate.setValidFromDate(null); - - // Create the SepaMandate, which fails. - SepaMandateDTO sepaMandateDTO = sepaMandateMapper.toDto(sepaMandate); - - restSepaMandateMockMvc.perform( - post("/api/sepa-mandates") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(sepaMandateDTO))) - .andExpect(status().isBadRequest()); - - List sepaMandateList = sepaMandateRepository.findAll(); - assertThat(sepaMandateList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void getAllSepaMandates() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList - restSepaMandateMockMvc.perform(get("/api/sepa-mandates?sort=id,desc")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(sepaMandate.getId().intValue()))) - .andExpect(jsonPath("$.[*].reference").value(hasItem(DEFAULT_REFERENCE))) - .andExpect(jsonPath("$.[*].iban").value(hasItem(DEFAULT_IBAN))) - .andExpect(jsonPath("$.[*].bic").value(hasItem(DEFAULT_BIC))) - .andExpect(jsonPath("$.[*].grantingDocumentDate").value(hasItem(DEFAULT_GRANTING_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].revokationDocumentDate").value(hasItem(DEFAULT_REVOKATION_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].validFromDate").value(hasItem(DEFAULT_VALID_FROM_DATE.toString()))) - .andExpect(jsonPath("$.[*].validUntilDate").value(hasItem(DEFAULT_VALID_UNTIL_DATE.toString()))) - .andExpect(jsonPath("$.[*].lastUsedDate").value(hasItem(DEFAULT_LAST_USED_DATE.toString()))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK))); - } - - @Test - @Transactional - public void getSepaMandate() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get the sepaMandate - restSepaMandateMockMvc.perform(get("/api/sepa-mandates/{id}", sepaMandate.getId())) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.id").value(sepaMandate.getId().intValue())) - .andExpect(jsonPath("$.reference").value(DEFAULT_REFERENCE)) - .andExpect(jsonPath("$.iban").value(DEFAULT_IBAN)) - .andExpect(jsonPath("$.bic").value(DEFAULT_BIC)) - .andExpect(jsonPath("$.grantingDocumentDate").value(DEFAULT_GRANTING_DOCUMENT_DATE.toString())) - .andExpect(jsonPath("$.revokationDocumentDate").value(DEFAULT_REVOKATION_DOCUMENT_DATE.toString())) - .andExpect(jsonPath("$.validFromDate").value(DEFAULT_VALID_FROM_DATE.toString())) - .andExpect(jsonPath("$.validUntilDate").value(DEFAULT_VALID_UNTIL_DATE.toString())) - .andExpect(jsonPath("$.lastUsedDate").value(DEFAULT_LAST_USED_DATE.toString())) - .andExpect(jsonPath("$.remark").value(DEFAULT_REMARK)); - } - - @Test - @Transactional - public void getAllSepaMandatesByReferenceIsEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where reference equals to DEFAULT_REFERENCE - defaultSepaMandateShouldBeFound("reference.equals=" + DEFAULT_REFERENCE); - - // Get all the sepaMandateList where reference equals to UPDATED_REFERENCE - defaultSepaMandateShouldNotBeFound("reference.equals=" + UPDATED_REFERENCE); - } - - @Test - @Transactional - public void getAllSepaMandatesByReferenceIsInShouldWork() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where reference in DEFAULT_REFERENCE or UPDATED_REFERENCE - defaultSepaMandateShouldBeFound("reference.in=" + DEFAULT_REFERENCE + "," + UPDATED_REFERENCE); - - // Get all the sepaMandateList where reference equals to UPDATED_REFERENCE - defaultSepaMandateShouldNotBeFound("reference.in=" + UPDATED_REFERENCE); - } - - @Test - @Transactional - public void getAllSepaMandatesByReferenceIsNullOrNotNull() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where reference is not null - defaultSepaMandateShouldBeFound("reference.specified=true"); - - // Get all the sepaMandateList where reference is null - defaultSepaMandateShouldNotBeFound("reference.specified=false"); - } - - @Test - @Transactional - public void getAllSepaMandatesByIbanIsEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where iban equals to DEFAULT_IBAN - defaultSepaMandateShouldBeFound("iban.equals=" + DEFAULT_IBAN); - - // Get all the sepaMandateList where iban equals to UPDATED_IBAN - defaultSepaMandateShouldNotBeFound("iban.equals=" + UPDATED_IBAN); - } - - @Test - @Transactional - public void getAllSepaMandatesByIbanIsInShouldWork() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where iban in DEFAULT_IBAN or UPDATED_IBAN - defaultSepaMandateShouldBeFound("iban.in=" + DEFAULT_IBAN + "," + UPDATED_IBAN); - - // Get all the sepaMandateList where iban equals to UPDATED_IBAN - defaultSepaMandateShouldNotBeFound("iban.in=" + UPDATED_IBAN); - } - - @Test - @Transactional - public void getAllSepaMandatesByIbanIsNullOrNotNull() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where iban is not null - defaultSepaMandateShouldBeFound("iban.specified=true"); - - // Get all the sepaMandateList where iban is null - defaultSepaMandateShouldNotBeFound("iban.specified=false"); - } - - @Test - @Transactional - public void getAllSepaMandatesByBicIsEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where bic equals to DEFAULT_BIC - defaultSepaMandateShouldBeFound("bic.equals=" + DEFAULT_BIC); - - // Get all the sepaMandateList where bic equals to UPDATED_BIC - defaultSepaMandateShouldNotBeFound("bic.equals=" + UPDATED_BIC); - } - - @Test - @Transactional - public void getAllSepaMandatesByBicIsInShouldWork() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where bic in DEFAULT_BIC or UPDATED_BIC - defaultSepaMandateShouldBeFound("bic.in=" + DEFAULT_BIC + "," + UPDATED_BIC); - - // Get all the sepaMandateList where bic equals to UPDATED_BIC - defaultSepaMandateShouldNotBeFound("bic.in=" + UPDATED_BIC); - } - - @Test - @Transactional - public void getAllSepaMandatesByBicIsNullOrNotNull() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where bic is not null - defaultSepaMandateShouldBeFound("bic.specified=true"); - - // Get all the sepaMandateList where bic is null - defaultSepaMandateShouldNotBeFound("bic.specified=false"); - } - - @Test - @Transactional - public void getAllSepaMandatesByGrantingDocumentDateIsEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where grantingDocumentDate equals to DEFAULT_GRANTING_DOCUMENT_DATE - defaultSepaMandateShouldBeFound("grantingDocumentDate.equals=" + DEFAULT_GRANTING_DOCUMENT_DATE); - - // Get all the sepaMandateList where grantingDocumentDate equals to UPDATED_GRANTING_DOCUMENT_DATE - defaultSepaMandateShouldNotBeFound("grantingDocumentDate.equals=" + UPDATED_GRANTING_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByGrantingDocumentDateIsInShouldWork() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where grantingDocumentDate in DEFAULT_GRANTING_DOCUMENT_DATE or - // UPDATED_GRANTING_DOCUMENT_DATE - defaultSepaMandateShouldBeFound( - "grantingDocumentDate.in=" + DEFAULT_GRANTING_DOCUMENT_DATE + "," + UPDATED_GRANTING_DOCUMENT_DATE); - - // Get all the sepaMandateList where grantingDocumentDate equals to UPDATED_GRANTING_DOCUMENT_DATE - defaultSepaMandateShouldNotBeFound("grantingDocumentDate.in=" + UPDATED_GRANTING_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByGrantingDocumentDateIsNullOrNotNull() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where grantingDocumentDate is not null - defaultSepaMandateShouldBeFound("grantingDocumentDate.specified=true"); - - // Get all the sepaMandateList where grantingDocumentDate is null - defaultSepaMandateShouldNotBeFound("grantingDocumentDate.specified=false"); - } - - @Test - @Transactional - public void getAllSepaMandatesByGrantingDocumentDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where grantingDocumentDate greater than or equals to DEFAULT_GRANTING_DOCUMENT_DATE - defaultSepaMandateShouldBeFound("grantingDocumentDate.greaterOrEqualThan=" + DEFAULT_GRANTING_DOCUMENT_DATE); - - // Get all the sepaMandateList where grantingDocumentDate greater than or equals to UPDATED_GRANTING_DOCUMENT_DATE - defaultSepaMandateShouldNotBeFound("grantingDocumentDate.greaterOrEqualThan=" + UPDATED_GRANTING_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByGrantingDocumentDateIsLessThanSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where grantingDocumentDate less than or equals to DEFAULT_GRANTING_DOCUMENT_DATE - defaultSepaMandateShouldNotBeFound("grantingDocumentDate.lessThan=" + DEFAULT_GRANTING_DOCUMENT_DATE); - - // Get all the sepaMandateList where grantingDocumentDate less than or equals to UPDATED_GRANTING_DOCUMENT_DATE - defaultSepaMandateShouldBeFound("grantingDocumentDate.lessThan=" + UPDATED_GRANTING_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByRevokationDocumentDateIsEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where revokationDocumentDate equals to DEFAULT_REVOKATION_DOCUMENT_DATE - defaultSepaMandateShouldBeFound("revokationDocumentDate.equals=" + DEFAULT_REVOKATION_DOCUMENT_DATE); - - // Get all the sepaMandateList where revokationDocumentDate equals to UPDATED_REVOKATION_DOCUMENT_DATE - defaultSepaMandateShouldNotBeFound("revokationDocumentDate.equals=" + UPDATED_REVOKATION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByRevokationDocumentDateIsInShouldWork() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where revokationDocumentDate in DEFAULT_REVOKATION_DOCUMENT_DATE or - // UPDATED_REVOKATION_DOCUMENT_DATE - defaultSepaMandateShouldBeFound( - "revokationDocumentDate.in=" + DEFAULT_REVOKATION_DOCUMENT_DATE + "," + UPDATED_REVOKATION_DOCUMENT_DATE); - - // Get all the sepaMandateList where revokationDocumentDate equals to UPDATED_REVOKATION_DOCUMENT_DATE - defaultSepaMandateShouldNotBeFound("revokationDocumentDate.in=" + UPDATED_REVOKATION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByRevokationDocumentDateIsNullOrNotNull() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where revokationDocumentDate is not null - defaultSepaMandateShouldBeFound("revokationDocumentDate.specified=true"); - - // Get all the sepaMandateList where revokationDocumentDate is null - defaultSepaMandateShouldNotBeFound("revokationDocumentDate.specified=false"); - } - - @Test - @Transactional - public void getAllSepaMandatesByRevokationDocumentDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where revokationDocumentDate greater than or equals to DEFAULT_REVOKATION_DOCUMENT_DATE - defaultSepaMandateShouldBeFound("revokationDocumentDate.greaterOrEqualThan=" + DEFAULT_REVOKATION_DOCUMENT_DATE); - - // Get all the sepaMandateList where revokationDocumentDate greater than or equals to UPDATED_REVOKATION_DOCUMENT_DATE - defaultSepaMandateShouldNotBeFound("revokationDocumentDate.greaterOrEqualThan=" + UPDATED_REVOKATION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByRevokationDocumentDateIsLessThanSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where revokationDocumentDate less than or equals to DEFAULT_REVOKATION_DOCUMENT_DATE - defaultSepaMandateShouldNotBeFound("revokationDocumentDate.lessThan=" + DEFAULT_REVOKATION_DOCUMENT_DATE); - - // Get all the sepaMandateList where revokationDocumentDate less than or equals to UPDATED_REVOKATION_DOCUMENT_DATE - defaultSepaMandateShouldBeFound("revokationDocumentDate.lessThan=" + UPDATED_REVOKATION_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidFromDateIsEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validFromDate equals to DEFAULT_VALID_FROM_DATE - defaultSepaMandateShouldBeFound("validFromDate.equals=" + DEFAULT_VALID_FROM_DATE); - - // Get all the sepaMandateList where validFromDate equals to UPDATED_VALID_FROM_DATE - defaultSepaMandateShouldNotBeFound("validFromDate.equals=" + UPDATED_VALID_FROM_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidFromDateIsInShouldWork() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validFromDate in DEFAULT_VALID_FROM_DATE or UPDATED_VALID_FROM_DATE - defaultSepaMandateShouldBeFound("validFromDate.in=" + DEFAULT_VALID_FROM_DATE + "," + UPDATED_VALID_FROM_DATE); - - // Get all the sepaMandateList where validFromDate equals to UPDATED_VALID_FROM_DATE - defaultSepaMandateShouldNotBeFound("validFromDate.in=" + UPDATED_VALID_FROM_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidFromDateIsNullOrNotNull() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validFromDate is not null - defaultSepaMandateShouldBeFound("validFromDate.specified=true"); - - // Get all the sepaMandateList where validFromDate is null - defaultSepaMandateShouldNotBeFound("validFromDate.specified=false"); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidFromDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validFromDate greater than or equals to DEFAULT_VALID_FROM_DATE - defaultSepaMandateShouldBeFound("validFromDate.greaterOrEqualThan=" + DEFAULT_VALID_FROM_DATE); - - // Get all the sepaMandateList where validFromDate greater than or equals to UPDATED_VALID_FROM_DATE - defaultSepaMandateShouldNotBeFound("validFromDate.greaterOrEqualThan=" + UPDATED_VALID_FROM_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidFromDateIsLessThanSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validFromDate less than or equals to DEFAULT_VALID_FROM_DATE - defaultSepaMandateShouldNotBeFound("validFromDate.lessThan=" + DEFAULT_VALID_FROM_DATE); - - // Get all the sepaMandateList where validFromDate less than or equals to UPDATED_VALID_FROM_DATE - defaultSepaMandateShouldBeFound("validFromDate.lessThan=" + UPDATED_VALID_FROM_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidUntilDateIsEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validUntilDate equals to DEFAULT_VALID_UNTIL_DATE - defaultSepaMandateShouldBeFound("validUntilDate.equals=" + DEFAULT_VALID_UNTIL_DATE); - - // Get all the sepaMandateList where validUntilDate equals to UPDATED_VALID_UNTIL_DATE - defaultSepaMandateShouldNotBeFound("validUntilDate.equals=" + UPDATED_VALID_UNTIL_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidUntilDateIsInShouldWork() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validUntilDate in DEFAULT_VALID_UNTIL_DATE or UPDATED_VALID_UNTIL_DATE - defaultSepaMandateShouldBeFound("validUntilDate.in=" + DEFAULT_VALID_UNTIL_DATE + "," + UPDATED_VALID_UNTIL_DATE); - - // Get all the sepaMandateList where validUntilDate equals to UPDATED_VALID_UNTIL_DATE - defaultSepaMandateShouldNotBeFound("validUntilDate.in=" + UPDATED_VALID_UNTIL_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidUntilDateIsNullOrNotNull() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validUntilDate is not null - defaultSepaMandateShouldBeFound("validUntilDate.specified=true"); - - // Get all the sepaMandateList where validUntilDate is null - defaultSepaMandateShouldNotBeFound("validUntilDate.specified=false"); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidUntilDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validUntilDate greater than or equals to DEFAULT_VALID_UNTIL_DATE - defaultSepaMandateShouldBeFound("validUntilDate.greaterOrEqualThan=" + DEFAULT_VALID_UNTIL_DATE); - - // Get all the sepaMandateList where validUntilDate greater than or equals to UPDATED_VALID_UNTIL_DATE - defaultSepaMandateShouldNotBeFound("validUntilDate.greaterOrEqualThan=" + UPDATED_VALID_UNTIL_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByValidUntilDateIsLessThanSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where validUntilDate less than or equals to DEFAULT_VALID_UNTIL_DATE - defaultSepaMandateShouldNotBeFound("validUntilDate.lessThan=" + DEFAULT_VALID_UNTIL_DATE); - - // Get all the sepaMandateList where validUntilDate less than or equals to UPDATED_VALID_UNTIL_DATE - defaultSepaMandateShouldBeFound("validUntilDate.lessThan=" + UPDATED_VALID_UNTIL_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByLastUsedDateIsEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where lastUsedDate equals to DEFAULT_LAST_USED_DATE - defaultSepaMandateShouldBeFound("lastUsedDate.equals=" + DEFAULT_LAST_USED_DATE); - - // Get all the sepaMandateList where lastUsedDate equals to UPDATED_LAST_USED_DATE - defaultSepaMandateShouldNotBeFound("lastUsedDate.equals=" + UPDATED_LAST_USED_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByLastUsedDateIsInShouldWork() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where lastUsedDate in DEFAULT_LAST_USED_DATE or UPDATED_LAST_USED_DATE - defaultSepaMandateShouldBeFound("lastUsedDate.in=" + DEFAULT_LAST_USED_DATE + "," + UPDATED_LAST_USED_DATE); - - // Get all the sepaMandateList where lastUsedDate equals to UPDATED_LAST_USED_DATE - defaultSepaMandateShouldNotBeFound("lastUsedDate.in=" + UPDATED_LAST_USED_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByLastUsedDateIsNullOrNotNull() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where lastUsedDate is not null - defaultSepaMandateShouldBeFound("lastUsedDate.specified=true"); - - // Get all the sepaMandateList where lastUsedDate is null - defaultSepaMandateShouldNotBeFound("lastUsedDate.specified=false"); - } - - @Test - @Transactional - public void getAllSepaMandatesByLastUsedDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where lastUsedDate greater than or equals to DEFAULT_LAST_USED_DATE - defaultSepaMandateShouldBeFound("lastUsedDate.greaterOrEqualThan=" + DEFAULT_LAST_USED_DATE); - - // Get all the sepaMandateList where lastUsedDate greater than or equals to UPDATED_LAST_USED_DATE - defaultSepaMandateShouldNotBeFound("lastUsedDate.greaterOrEqualThan=" + UPDATED_LAST_USED_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByLastUsedDateIsLessThanSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where lastUsedDate less than or equals to DEFAULT_LAST_USED_DATE - defaultSepaMandateShouldNotBeFound("lastUsedDate.lessThan=" + DEFAULT_LAST_USED_DATE); - - // Get all the sepaMandateList where lastUsedDate less than or equals to UPDATED_LAST_USED_DATE - defaultSepaMandateShouldBeFound("lastUsedDate.lessThan=" + UPDATED_LAST_USED_DATE); - } - - @Test - @Transactional - public void getAllSepaMandatesByRemarkIsEqualToSomething() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where remark equals to DEFAULT_REMARK - defaultSepaMandateShouldBeFound("remark.equals=" + DEFAULT_REMARK); - - // Get all the sepaMandateList where remark equals to UPDATED_REMARK - defaultSepaMandateShouldNotBeFound("remark.equals=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllSepaMandatesByRemarkIsInShouldWork() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where remark in DEFAULT_REMARK or UPDATED_REMARK - defaultSepaMandateShouldBeFound("remark.in=" + DEFAULT_REMARK + "," + UPDATED_REMARK); - - // Get all the sepaMandateList where remark equals to UPDATED_REMARK - defaultSepaMandateShouldNotBeFound("remark.in=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllSepaMandatesByRemarkIsNullOrNotNull() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - // Get all the sepaMandateList where remark is not null - defaultSepaMandateShouldBeFound("remark.specified=true"); - - // Get all the sepaMandateList where remark is null - defaultSepaMandateShouldNotBeFound("remark.specified=false"); - } - - @Test - @Transactional - public void getAllSepaMandatesByCustomerIsEqualToSomething() throws Exception { - // Initialize the database - Customer customer = CustomerResourceIntTest.createPersistentEntity(em); - sepaMandate.setCustomer(customer); - sepaMandateRepository.saveAndFlush(sepaMandate); - Long customerId = customer.getId(); - - // Get all the sepaMandateList where customer equals to customerId - defaultSepaMandateShouldBeFound("customerId.equals=" + customerId); - - // Get all the sepaMandateList where customer equals to customerId + 1 - defaultSepaMandateShouldNotBeFound("customerId.equals=" + (customerId + 1)); - } - - /** - * Executes the search, and checks that the default entity is returned - */ - private void defaultSepaMandateShouldBeFound(String filter) throws Exception { - restSepaMandateMockMvc.perform(get("/api/sepa-mandates?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(sepaMandate.getId().intValue()))) - .andExpect(jsonPath("$.[*].reference").value(hasItem(DEFAULT_REFERENCE))) - .andExpect(jsonPath("$.[*].iban").value(hasItem(DEFAULT_IBAN))) - .andExpect(jsonPath("$.[*].bic").value(hasItem(DEFAULT_BIC))) - .andExpect(jsonPath("$.[*].grantingDocumentDate").value(hasItem(DEFAULT_GRANTING_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].revokationDocumentDate").value(hasItem(DEFAULT_REVOKATION_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].validFromDate").value(hasItem(DEFAULT_VALID_FROM_DATE.toString()))) - .andExpect(jsonPath("$.[*].validUntilDate").value(hasItem(DEFAULT_VALID_UNTIL_DATE.toString()))) - .andExpect(jsonPath("$.[*].lastUsedDate").value(hasItem(DEFAULT_LAST_USED_DATE.toString()))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK))); - - // Check, that the count call also returns 1 - restSepaMandateMockMvc.perform(get("/api/sepa-mandates/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("1")); - } - - /** - * Executes the search, and checks that the default entity is not returned - */ - private void defaultSepaMandateShouldNotBeFound(String filter) throws Exception { - restSepaMandateMockMvc.perform(get("/api/sepa-mandates?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$").isArray()) - .andExpect(jsonPath("$").isEmpty()); - - // Check, that the count call also returns 0 - restSepaMandateMockMvc.perform(get("/api/sepa-mandates/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("0")); - } - - @Test - @Transactional - public void getNonExistingSepaMandate() throws Exception { - // Get the sepaMandate - restSepaMandateMockMvc.perform(get("/api/sepa-mandates/{id}", Long.MAX_VALUE)) - .andExpect(status().isNotFound()); - } - - @Test - @Transactional - public void updateSepaMandate() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - int databaseSizeBeforeUpdate = sepaMandateRepository.findAll().size(); - - // Update the sepaMandate - SepaMandate updatedSepaMandate = sepaMandateRepository.findById(sepaMandate.getId()).get(); - // Disconnect from session so that the updates on updatedSepaMandate are not directly saved in db - em.detach(updatedSepaMandate); - updatedSepaMandate - .revokationDocumentDate(UPDATED_REVOKATION_DOCUMENT_DATE) - .validUntilDate(UPDATED_VALID_UNTIL_DATE) - .lastUsedDate(UPDATED_LAST_USED_DATE) - .remark(UPDATED_REMARK); - SepaMandateDTO sepaMandateDTO = sepaMandateMapper.toDto(updatedSepaMandate); - - restSepaMandateMockMvc.perform( - put("/api/sepa-mandates") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(sepaMandateDTO))) - .andExpect(status().isOk()); - - // Validate the SepaMandate in the database - List sepaMandateList = sepaMandateRepository.findAll(); - assertThat(sepaMandateList).hasSize(databaseSizeBeforeUpdate); - SepaMandate testSepaMandate = sepaMandateList.get(sepaMandateList.size() - 1); - assertThat(testSepaMandate.getReference()).isEqualTo(DEFAULT_REFERENCE); - assertThat(testSepaMandate.getIban()).isEqualTo(DEFAULT_IBAN); - assertThat(testSepaMandate.getBic()).isEqualTo(DEFAULT_BIC); - assertThat(testSepaMandate.getGrantingDocumentDate()).isEqualTo(DEFAULT_GRANTING_DOCUMENT_DATE); - assertThat(testSepaMandate.getRevokationDocumentDate()).isEqualTo(UPDATED_REVOKATION_DOCUMENT_DATE); - assertThat(testSepaMandate.getValidFromDate()).isEqualTo(DEFAULT_VALID_FROM_DATE); - assertThat(testSepaMandate.getValidUntilDate()).isEqualTo(UPDATED_VALID_UNTIL_DATE); - assertThat(testSepaMandate.getLastUsedDate()).isEqualTo(UPDATED_LAST_USED_DATE); - assertThat(testSepaMandate.getRemark()).isEqualTo(UPDATED_REMARK); - } - - @Test - @Transactional - public void updateNonExistingSepaMandate() throws Exception { - int databaseSizeBeforeUpdate = sepaMandateRepository.findAll().size(); - - // Create the SepaMandate - SepaMandateDTO sepaMandateDTO = sepaMandateMapper.toDto(sepaMandate); - - // If the entity doesn't have an ID, it will throw BadRequestAlertException - restSepaMandateMockMvc.perform( - put("/api/sepa-mandates") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(sepaMandateDTO))) - .andExpect(status().isBadRequest()); - - // Validate the SepaMandate in the database - List sepaMandateList = sepaMandateRepository.findAll(); - assertThat(sepaMandateList).hasSize(databaseSizeBeforeUpdate); - } - - @Test - @Transactional - public void deleteSepaMandate() throws Exception { - // Initialize the database - sepaMandateRepository.saveAndFlush(sepaMandate); - - int databaseSizeBeforeDelete = sepaMandateRepository.findAll().size(); - - // Delete the sepaMandate - restSepaMandateMockMvc.perform( - delete("/api/sepa-mandates/{id}", sepaMandate.getId()) - .accept(TestUtil.APPLICATION_JSON_UTF8)) - .andExpect(status().isOk()); - - // Validate the database is empty - List sepaMandateList = sepaMandateRepository.findAll(); - assertThat(sepaMandateList).hasSize(databaseSizeBeforeDelete - 1); - } - - @Test - @Transactional - public void equalsVerifier() throws Exception { - TestUtil.equalsVerifier(SepaMandate.class); - SepaMandate sepaMandate1 = new SepaMandate(); - sepaMandate1.setId(1L); - SepaMandate sepaMandate2 = new SepaMandate(); - sepaMandate2.setId(sepaMandate1.getId()); - assertThat(sepaMandate1).isEqualTo(sepaMandate2); - sepaMandate2.setId(2L); - assertThat(sepaMandate1).isNotEqualTo(sepaMandate2); - sepaMandate1.setId(null); - assertThat(sepaMandate1).isNotEqualTo(sepaMandate2); - } - - @Test - @Transactional - public void dtoEqualsVerifier() throws Exception { - TestUtil.equalsVerifier(SepaMandateDTO.class); - SepaMandateDTO sepaMandateDTO1 = new SepaMandateDTO(); - sepaMandateDTO1.setId(1L); - SepaMandateDTO sepaMandateDTO2 = new SepaMandateDTO(); - assertThat(sepaMandateDTO1).isNotEqualTo(sepaMandateDTO2); - sepaMandateDTO2.setId(sepaMandateDTO1.getId()); - assertThat(sepaMandateDTO1).isEqualTo(sepaMandateDTO2); - sepaMandateDTO2.setId(2L); - assertThat(sepaMandateDTO1).isNotEqualTo(sepaMandateDTO2); - sepaMandateDTO1.setId(null); - assertThat(sepaMandateDTO1).isNotEqualTo(sepaMandateDTO2); - } - - @Test - @Transactional - public void testEntityFromId() { - assertThat(sepaMandateMapper.fromId(42L).getId()).isEqualTo(42); - assertThat(sepaMandateMapper.fromId(null)).isNull(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/SepaMandateResourceUnitTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/SepaMandateResourceUnitTest.java deleted file mode 100644 index d320486b..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/SepaMandateResourceUnitTest.java +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.ThrowableAssert.catchThrowable; - -import org.hostsharing.hsadminng.service.dto.SepaMandateDTO; -import org.hostsharing.hsadminng.service.dto.SepaMandateDTOUnitTest; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -// Currently this class tests mostly special 'bad paths' -// which make little sense to test in *ResourceIntTest. -public class SepaMandateResourceUnitTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @InjectMocks - private SepaMandateResource sepaMandateResource; - - @Test - public void createSepaMandateWithoutIdThrowsBadRequestException() { - - // given - final SepaMandateDTO givenDto = SepaMandateDTOUnitTest.createRandomDTO(null, 1L); - - // when - final Throwable actual = catchThrowable(() -> sepaMandateResource.updateSepaMandate(givenDto)); - - // then - assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> { - assertThat(bre.getErrorKey()).isEqualTo("idnull"); - assertThat(bre.getParam()).isEqualTo("sepaMandate"); - }); - } - - @Test - public void createSepaMandateWithIdThrowsBadRequestException() { - - // given - final SepaMandateDTO givenDto = SepaMandateDTOUnitTest.createRandomDTO(2L, 1L); - - // when - final Throwable actual = catchThrowable(() -> sepaMandateResource.createSepaMandate(givenDto)); - - // then - assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> { - assertThat(bre.getErrorKey()).isEqualTo("idexists"); - assertThat(bre.getParam()).isEqualTo("sepaMandate"); - }); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/ShareResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/ShareResourceIntTest.java deleted file mode 100644 index 3feef446..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/ShareResourceIntTest.java +++ /dev/null @@ -1,802 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.hasItem; -import static org.hostsharing.hsadminng.web.rest.TestUtil.createFormattingConversionService; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Membership; -import org.hostsharing.hsadminng.domain.Share; -import org.hostsharing.hsadminng.domain.enumeration.ShareAction; -import org.hostsharing.hsadminng.repository.ShareRepository; -import org.hostsharing.hsadminng.service.ShareQueryService; -import org.hostsharing.hsadminng.service.ShareService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock; -import org.hostsharing.hsadminng.service.dto.ShareDTO; -import org.hostsharing.hsadminng.service.mapper.ShareMapper; -import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.Validator; - -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.List; - -import javax.persistence.EntityManager; - -/** - * Test class for the ShareResource REST controller. - * - * @see ShareResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class ShareResourceIntTest { - - private static final LocalDate DEFAULT_DOCUMENT_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_DOCUMENT_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final LocalDate DEFAULT_VALUE_DATE = LocalDate.ofEpochDay(0L); - private static final LocalDate UPDATED_VALUE_DATE = LocalDate.now(ZoneId.systemDefault()); - - private static final ShareAction DEFAULT_ACTION = ShareAction.SUBSCRIPTION; - private static final ShareAction UPDATED_ACTION = ShareAction.CANCELLATION; - - private static final Integer DEFAULT_QUANTITY = 1; - private static final Integer UPDATED_QUANTITY = 2; - - private static final String DEFAULT_REMARK = "AAAAAAAAAA"; - private static final String UPDATED_REMARK = "BBBBBBBBBB"; - - @Autowired - private ShareRepository shareRepository; - - @Autowired - private ShareMapper shareMapper; - - @Autowired - private ShareService shareService; - - @Autowired - private ShareQueryService shareQueryService; - - @Autowired - private MappingJackson2HttpMessageConverter jacksonMessageConverter; - - @Autowired - private PageableHandlerMethodArgumentResolver pageableArgumentResolver; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - @Autowired - private EntityManager em; - - @Autowired - private Validator validator; - - @MockBean - private UserRoleAssignmentService userRoleAssignmentService; - - private MockMvc restShareMockMvc; - - private Share share; - - @Before - public void setup() { - SecurityContextMock.usingMock(userRoleAssignmentService) - .havingAuthenticatedUser() - .withAuthority(Role.Admin.ROLE.authority()); - - MockitoAnnotations.initMocks(this); - final ShareResource shareResource = new ShareResource(shareService, shareQueryService); - this.restShareMockMvc = MockMvcBuilders.standaloneSetup(shareResource) - .setCustomArgumentResolvers(pageableArgumentResolver) - .setControllerAdvice(exceptionTranslator) - .setConversionService(createFormattingConversionService()) - .setMessageConverters(jacksonMessageConverter) - .setValidator(validator) - .build(); - } - - /** - * Create an entity for this test. - *

- * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static Share createEntity(EntityManager em) { - Share share = new Share() - .documentDate(DEFAULT_DOCUMENT_DATE) - .valueDate(DEFAULT_VALUE_DATE) - .action(DEFAULT_ACTION) - .quantity(DEFAULT_QUANTITY) - .remark(DEFAULT_REMARK); - // Add required entity - Membership membership = MembershipResourceIntTest.createEntity(em); - em.persist(membership); - em.flush(); - share.setMembership(membership); - return share; - } - - /** - * Create a persistent entity related to the given persistent membership for testing purposes. - *

- * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static Share createPersistentEntity(EntityManager em, final Membership membership) { - Share share = new Share() - .documentDate(DEFAULT_DOCUMENT_DATE) - .valueDate(DEFAULT_VALUE_DATE) - .action(DEFAULT_ACTION) - .quantity(DEFAULT_QUANTITY) - .remark(DEFAULT_REMARK); - // Add required entity - share.setMembership(membership); - membership.addShare(share); - em.persist(share); - em.flush(); - return share; - } - - @Before - public void initTest() { - share = createEntity(em); - } - - @Test - @Transactional - public void createShare() throws Exception { - int databaseSizeBeforeCreate = shareRepository.findAll().size(); - - // Create the Share - ShareDTO shareDTO = shareMapper.toDto(share); - shareDTO.setMembershipDisplayLabel(null); - restShareMockMvc.perform( - post("/api/shares") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(shareDTO))) - .andExpect(status().isCreated()); - - // Validate the Share in the database - List shareList = shareRepository.findAll(); - assertThat(shareList).hasSize(databaseSizeBeforeCreate + 1); - Share testShare = shareList.get(shareList.size() - 1); - assertThat(testShare.getDocumentDate()).isEqualTo(DEFAULT_DOCUMENT_DATE); - assertThat(testShare.getValueDate()).isEqualTo(DEFAULT_VALUE_DATE); - assertThat(testShare.getAction()).isEqualTo(DEFAULT_ACTION); - assertThat(testShare.getQuantity()).isEqualTo(DEFAULT_QUANTITY); - assertThat(testShare.getRemark()).isEqualTo(DEFAULT_REMARK); - } - - @Test - @Transactional - public void createShareWithIdForNonExistingEntity() throws Exception { - int databaseSizeBeforeCreate = shareRepository.findAll().size(); - - // Create the Share with an ID - share.setId(1L); - ShareDTO shareDTO = shareMapper.toDto(share); - - // An entity with an existing ID cannot be created, so this API call must fail - restShareMockMvc.perform( - post("/api/shares") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(shareDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Share in the database - List shareList = shareRepository.findAll(); - assertThat(shareList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void createShareWithExistingExistingEntity() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - int databaseSizeBeforeCreate = shareRepository.findAll().size(); - - // Create the Share with the ID of an existing ID - ShareDTO shareDTO = shareMapper.toDto(share); - - // An entity with an existing ID cannot be created, so this API call must fail - restShareMockMvc.perform( - post("/api/shares") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(shareDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Share in the database - List assetList = shareRepository.findAll(); - assertThat(assetList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void checkDocumentDateIsRequired() throws Exception { - int databaseSizeBeforeTest = shareRepository.findAll().size(); - // set the field null - share.setDocumentDate(null); - - // Create the Share, which fails. - ShareDTO shareDTO = shareMapper.toDto(share); - - restShareMockMvc.perform( - post("/api/shares") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(shareDTO))) - .andExpect(status().isBadRequest()); - - List shareList = shareRepository.findAll(); - assertThat(shareList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkValueDateIsRequired() throws Exception { - int databaseSizeBeforeTest = shareRepository.findAll().size(); - // set the field null - share.setValueDate(null); - - // Create the Share, which fails. - ShareDTO shareDTO = shareMapper.toDto(share); - - restShareMockMvc.perform( - post("/api/shares") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(shareDTO))) - .andExpect(status().isBadRequest()); - - List shareList = shareRepository.findAll(); - assertThat(shareList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkActionIsRequired() throws Exception { - int databaseSizeBeforeTest = shareRepository.findAll().size(); - // set the field null - share.setAction(null); - - // Create the Share, which fails. - ShareDTO shareDTO = shareMapper.toDto(share); - - restShareMockMvc.perform( - post("/api/shares") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(shareDTO))) - .andExpect(status().isBadRequest()); - - List shareList = shareRepository.findAll(); - assertThat(shareList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkQuantityIsRequired() throws Exception { - int databaseSizeBeforeTest = shareRepository.findAll().size(); - // set the field null - share.setQuantity(null); - - // Create the Share, which fails. - ShareDTO shareDTO = shareMapper.toDto(share); - - restShareMockMvc.perform( - post("/api/shares") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(shareDTO))) - .andExpect(status().isBadRequest()); - - List shareList = shareRepository.findAll(); - assertThat(shareList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void getAllShares() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList - restShareMockMvc.perform(get("/api/shares?sort=id,desc")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(share.getId().intValue()))) - .andExpect(jsonPath("$.[*].documentDate").value(hasItem(DEFAULT_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].valueDate").value(hasItem(DEFAULT_VALUE_DATE.toString()))) - .andExpect(jsonPath("$.[*].action").value(hasItem(DEFAULT_ACTION.toString()))) - .andExpect(jsonPath("$.[*].quantity").value(hasItem(DEFAULT_QUANTITY))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK.toString()))); - } - - @Test - @Transactional - public void getShare() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get the share - restShareMockMvc.perform(get("/api/shares/{id}", share.getId())) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.id").value(share.getId().intValue())) - .andExpect(jsonPath("$.documentDate").value(DEFAULT_DOCUMENT_DATE.toString())) - .andExpect(jsonPath("$.valueDate").value(DEFAULT_VALUE_DATE.toString())) - .andExpect(jsonPath("$.action").value(DEFAULT_ACTION.toString())) - .andExpect(jsonPath("$.quantity").value(DEFAULT_QUANTITY)) - .andExpect(jsonPath("$.remark").value(DEFAULT_REMARK.toString())); - } - - @Test - @Transactional - public void getAllSharesByDocumentDateIsEqualToSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where documentDate equals to DEFAULT_DOCUMENT_DATE - defaultShareShouldBeFound("documentDate.equals=" + DEFAULT_DOCUMENT_DATE); - - // Get all the shareList where documentDate equals to UPDATED_DOCUMENT_DATE - defaultShareShouldNotBeFound("documentDate.equals=" + UPDATED_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSharesByDocumentDateIsInShouldWork() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where documentDate in DEFAULT_DOCUMENT_DATE or UPDATED_DOCUMENT_DATE - defaultShareShouldBeFound("documentDate.in=" + DEFAULT_DOCUMENT_DATE + "," + UPDATED_DOCUMENT_DATE); - - // Get all the shareList where documentDate equals to UPDATED_DOCUMENT_DATE - defaultShareShouldNotBeFound("documentDate.in=" + UPDATED_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSharesByDocumentDateIsNullOrNotNull() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where documentDate is not null - defaultShareShouldBeFound("documentDate.specified=true"); - - // Get all the shareList where documentDate is null - defaultShareShouldNotBeFound("documentDate.specified=false"); - } - - @Test - @Transactional - public void getAllSharesByDocumentDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where documentDate greater than or equals to DEFAULT_DOCUMENT_DATE - defaultShareShouldBeFound("documentDate.greaterOrEqualThan=" + DEFAULT_DOCUMENT_DATE); - - // Get all the shareList where documentDate greater than or equals to UPDATED_DOCUMENT_DATE - defaultShareShouldNotBeFound("documentDate.greaterOrEqualThan=" + UPDATED_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSharesByDocumentDateIsLessThanSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where documentDate less than or equals to DEFAULT_DOCUMENT_DATE - defaultShareShouldNotBeFound("documentDate.lessThan=" + DEFAULT_DOCUMENT_DATE); - - // Get all the shareList where documentDate less than or equals to UPDATED_DOCUMENT_DATE - defaultShareShouldBeFound("documentDate.lessThan=" + UPDATED_DOCUMENT_DATE); - } - - @Test - @Transactional - public void getAllSharesByValueDateIsEqualToSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where valueDate equals to DEFAULT_VALUE_DATE - defaultShareShouldBeFound("valueDate.equals=" + DEFAULT_VALUE_DATE); - - // Get all the shareList where valueDate equals to UPDATED_VALUE_DATE - defaultShareShouldNotBeFound("valueDate.equals=" + UPDATED_VALUE_DATE); - } - - @Test - @Transactional - public void getAllSharesByValueDateIsInShouldWork() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where valueDate in DEFAULT_VALUE_DATE or UPDATED_VALUE_DATE - defaultShareShouldBeFound("valueDate.in=" + DEFAULT_VALUE_DATE + "," + UPDATED_VALUE_DATE); - - // Get all the shareList where valueDate equals to UPDATED_VALUE_DATE - defaultShareShouldNotBeFound("valueDate.in=" + UPDATED_VALUE_DATE); - } - - @Test - @Transactional - public void getAllSharesByValueDateIsNullOrNotNull() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where valueDate is not null - defaultShareShouldBeFound("valueDate.specified=true"); - - // Get all the shareList where valueDate is null - defaultShareShouldNotBeFound("valueDate.specified=false"); - } - - @Test - @Transactional - public void getAllSharesByValueDateIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where valueDate greater than or equals to DEFAULT_VALUE_DATE - defaultShareShouldBeFound("valueDate.greaterOrEqualThan=" + DEFAULT_VALUE_DATE); - - // Get all the shareList where valueDate greater than or equals to UPDATED_VALUE_DATE - defaultShareShouldNotBeFound("valueDate.greaterOrEqualThan=" + UPDATED_VALUE_DATE); - } - - @Test - @Transactional - public void getAllSharesByValueDateIsLessThanSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where valueDate less than or equals to DEFAULT_VALUE_DATE - defaultShareShouldNotBeFound("valueDate.lessThan=" + DEFAULT_VALUE_DATE); - - // Get all the shareList where valueDate less than or equals to UPDATED_VALUE_DATE - defaultShareShouldBeFound("valueDate.lessThan=" + UPDATED_VALUE_DATE); - } - - @Test - @Transactional - public void getAllSharesByActionIsEqualToSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where action equals to DEFAULT_ACTION - defaultShareShouldBeFound("action.equals=" + DEFAULT_ACTION); - - // Get all the shareList where action equals to UPDATED_ACTION - defaultShareShouldNotBeFound("action.equals=" + UPDATED_ACTION); - } - - @Test - @Transactional - public void getAllSharesByActionIsInShouldWork() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where action in DEFAULT_ACTION or UPDATED_ACTION - defaultShareShouldBeFound("action.in=" + DEFAULT_ACTION + "," + UPDATED_ACTION); - - // Get all the shareList where action equals to UPDATED_ACTION - defaultShareShouldNotBeFound("action.in=" + UPDATED_ACTION); - } - - @Test - @Transactional - public void getAllSharesByActionIsNullOrNotNull() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where action is not null - defaultShareShouldBeFound("action.specified=true"); - - // Get all the shareList where action is null - defaultShareShouldNotBeFound("action.specified=false"); - } - - @Test - @Transactional - public void getAllSharesByQuantityIsEqualToSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where quantity equals to DEFAULT_QUANTITY - defaultShareShouldBeFound("quantity.equals=" + DEFAULT_QUANTITY); - - // Get all the shareList where quantity equals to UPDATED_QUANTITY - defaultShareShouldNotBeFound("quantity.equals=" + UPDATED_QUANTITY); - } - - @Test - @Transactional - public void getAllSharesByQuantityIsInShouldWork() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where quantity in DEFAULT_QUANTITY or UPDATED_QUANTITY - defaultShareShouldBeFound("quantity.in=" + DEFAULT_QUANTITY + "," + UPDATED_QUANTITY); - - // Get all the shareList where quantity equals to UPDATED_QUANTITY - defaultShareShouldNotBeFound("quantity.in=" + UPDATED_QUANTITY); - } - - @Test - @Transactional - public void getAllSharesByQuantityIsNullOrNotNull() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where quantity is not null - defaultShareShouldBeFound("quantity.specified=true"); - - // Get all the shareList where quantity is null - defaultShareShouldNotBeFound("quantity.specified=false"); - } - - @Test - @Transactional - public void getAllSharesByQuantityIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where quantity greater than or equals to DEFAULT_QUANTITY - defaultShareShouldBeFound("quantity.greaterOrEqualThan=" + DEFAULT_QUANTITY); - - // Get all the shareList where quantity greater than or equals to UPDATED_QUANTITY - defaultShareShouldNotBeFound("quantity.greaterOrEqualThan=" + UPDATED_QUANTITY); - } - - @Test - @Transactional - public void getAllSharesByQuantityIsLessThanSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where quantity less than or equals to DEFAULT_QUANTITY - defaultShareShouldNotBeFound("quantity.lessThan=" + DEFAULT_QUANTITY); - - // Get all the shareList where quantity less than or equals to UPDATED_QUANTITY - defaultShareShouldBeFound("quantity.lessThan=" + UPDATED_QUANTITY); - } - - @Test - @Transactional - public void getAllSharesByRemarkIsEqualToSomething() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where remark equals to DEFAULT_REMARK - defaultShareShouldBeFound("remark.equals=" + DEFAULT_REMARK); - - // Get all the shareList where remark equals to UPDATED_REMARK - defaultShareShouldNotBeFound("remark.equals=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllSharesByRemarkIsInShouldWork() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where remark in DEFAULT_REMARK or UPDATED_REMARK - defaultShareShouldBeFound("remark.in=" + DEFAULT_REMARK + "," + UPDATED_REMARK); - - // Get all the shareList where remark equals to UPDATED_REMARK - defaultShareShouldNotBeFound("remark.in=" + UPDATED_REMARK); - } - - @Test - @Transactional - public void getAllSharesByRemarkIsNullOrNotNull() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - // Get all the shareList where remark is not null - defaultShareShouldBeFound("remark.specified=true"); - - // Get all the shareList where remark is null - defaultShareShouldNotBeFound("remark.specified=false"); - } - - @Test - @Transactional - public void getAllSharesByMembershipIsEqualToSomething() throws Exception { - // Initialize the database - Membership membership = MembershipResourceIntTest - .createPersistentEntity(em, CustomerResourceIntTest.createPersistentEntity(em)); - share.setMembership(membership); - shareRepository.saveAndFlush(share); - Long membershipId = membership.getId(); - - // Get all the shareList where membership equals to membershipId - defaultShareShouldBeFound("membershipId.equals=" + membershipId); - - // Get all the shareList where membership equals to membershipId + 1 - defaultShareShouldNotBeFound("membershipId.equals=" + (membershipId + 1)); - } - - /** - * Executes the search, and checks that the default entity is returned - */ - private void defaultShareShouldBeFound(String filter) throws Exception { - restShareMockMvc.perform(get("/api/shares?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(share.getId().intValue()))) - .andExpect(jsonPath("$.[*].documentDate").value(hasItem(DEFAULT_DOCUMENT_DATE.toString()))) - .andExpect(jsonPath("$.[*].valueDate").value(hasItem(DEFAULT_VALUE_DATE.toString()))) - .andExpect(jsonPath("$.[*].action").value(hasItem(DEFAULT_ACTION.toString()))) - .andExpect(jsonPath("$.[*].quantity").value(hasItem(DEFAULT_QUANTITY))) - .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK))); - - // Check, that the count call also returns 1 - restShareMockMvc.perform(get("/api/shares/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("1")); - } - - /** - * Executes the search, and checks that the default entity is not returned - */ - private void defaultShareShouldNotBeFound(String filter) throws Exception { - restShareMockMvc.perform(get("/api/shares?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$").isArray()) - .andExpect(jsonPath("$").isEmpty()); - - // Check, that the count call also returns 0 - restShareMockMvc.perform(get("/api/shares/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("0")); - } - - @Test - @Transactional - public void getNonExistingShare() throws Exception { - // Get the share - restShareMockMvc.perform(get("/api/shares/{id}", Long.MAX_VALUE)) - .andExpect(status().isNotFound()); - } - - @Test - @Transactional - public void updateShare() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - int databaseSizeBeforeUpdate = shareRepository.findAll().size(); - - // Update the share - Share updatedShare = shareRepository.findById(share.getId()).get(); - // Disconnect from session so that the updates on updatedShare are not directly saved in db - em.detach(updatedShare); - updatedShare - .documentDate(UPDATED_DOCUMENT_DATE) - .valueDate(UPDATED_VALUE_DATE) - .action(UPDATED_ACTION) - .quantity(UPDATED_QUANTITY) - .remark(UPDATED_REMARK); - ShareDTO shareDTO = shareMapper.toDto(updatedShare); - - restShareMockMvc.perform( - put("/api/shares") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(shareDTO))) - .andExpect(status().isBadRequest()); - - // Validate the database is unchanged - List shareList = shareRepository.findAll(); - assertThat(shareList).hasSize(databaseSizeBeforeUpdate); - - Share testShare = shareList.get(shareList.size() - 1); - assertThat(testShare.getDocumentDate()).isEqualTo(DEFAULT_DOCUMENT_DATE); - assertThat(testShare.getValueDate()).isEqualTo(DEFAULT_VALUE_DATE); - assertThat(testShare.getAction()).isEqualTo(DEFAULT_ACTION); - assertThat(testShare.getQuantity()).isEqualTo(DEFAULT_QUANTITY); - assertThat(testShare.getRemark()).isEqualTo(DEFAULT_REMARK); - } - - @Test - @Transactional - public void updateNonExistingShare() throws Exception { - int databaseSizeBeforeUpdate = shareRepository.findAll().size(); - - // Create the Share - ShareDTO shareDTO = shareMapper.toDto(share); - - // If the entity doesn't have an ID, it will throw BadRequestAlertException - restShareMockMvc.perform( - put("/api/shares") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(shareDTO))) - .andExpect(status().isBadRequest()); - - // Validate the Share in the database - List shareList = shareRepository.findAll(); - assertThat(shareList).hasSize(databaseSizeBeforeUpdate); - } - - @Test - @Transactional - public void deleteShare() throws Exception { - // Initialize the database - shareRepository.saveAndFlush(share); - - int databaseSizeBeforeDelete = shareRepository.findAll().size(); - - // Delete the share - restShareMockMvc.perform( - delete("/api/shares/{id}", share.getId()) - .accept(TestUtil.APPLICATION_JSON_UTF8)) - .andExpect(status().isBadRequest()); - - // Validate the database is unchanged - List shareList = shareRepository.findAll(); - assertThat(shareList).hasSize(databaseSizeBeforeDelete); - } - - @Test - @Transactional - public void equalsVerifier() throws Exception { - TestUtil.equalsVerifier(Share.class); - Share share1 = new Share(); - share1.setId(1L); - Share share2 = new Share(); - share2.setId(share1.getId()); - assertThat(share1).isEqualTo(share2); - share2.setId(2L); - assertThat(share1).isNotEqualTo(share2); - share1.setId(null); - assertThat(share1).isNotEqualTo(share2); - } - - @Test - @Transactional - public void dtoEqualsVerifier() throws Exception { - TestUtil.equalsVerifier(ShareDTO.class); - ShareDTO shareDTO1 = new ShareDTO(); - shareDTO1.setId(1L); - ShareDTO shareDTO2 = new ShareDTO(); - assertThat(shareDTO1).isNotEqualTo(shareDTO2); - shareDTO2.setId(shareDTO1.getId()); - assertThat(shareDTO1).isEqualTo(shareDTO2); - shareDTO2.setId(2L); - assertThat(shareDTO1).isNotEqualTo(shareDTO2); - shareDTO1.setId(null); - assertThat(shareDTO1).isNotEqualTo(shareDTO2); - } - - @Test - @Transactional - public void testEntityFromId() { - assertThat(shareMapper.fromId(42L).getId()).isEqualTo(42); - assertThat(shareMapper.fromId(null)).isNull(); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/TestUtil.java b/src/test/java/org/hostsharing/hsadminng/web/rest/TestUtil.java deleted file mode 100644 index b4d22bed..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/TestUtil.java +++ /dev/null @@ -1,147 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; -import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; -import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.format.support.FormattingConversionService; -import org.springframework.http.MediaType; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.time.ZonedDateTime; -import java.time.format.DateTimeParseException; - -/** - * Utility class for testing REST controllers. - */ -public final class TestUtil { - - private static final ObjectMapper mapper = createObjectMapper(); - - /** MediaType for JSON UTF8 */ - public static final MediaType APPLICATION_JSON_UTF8 = new MediaType( - MediaType.APPLICATION_JSON.getType(), - MediaType.APPLICATION_JSON.getSubtype(), - StandardCharsets.UTF_8); - - private static ObjectMapper createObjectMapper() { - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - mapper.registerModule(new JavaTimeModule()); - return mapper; - } - - /** - * Convert an object to JSON byte array. - * - * @param object - * the object to convert - * @return the JSON byte array - * @throws IOException - */ - public static byte[] convertObjectToJsonBytes(Object object) - throws IOException { - return mapper.writeValueAsBytes(object); - } - - /** - * Create a byte array with a specific size filled with specified data. - * - * @param size the size of the byte array - * @param data the data to put in the byte array - * @return the JSON byte array - */ - public static byte[] createByteArray(int size, String data) { - byte[] byteArray = new byte[size]; - for (int i = 0; i < size; i++) { - byteArray[i] = Byte.parseByte(data, 2); - } - return byteArray; - } - - /** - * A matcher that tests that the examined string represents the same instant as the reference datetime. - */ - public static class ZonedDateTimeMatcher extends TypeSafeDiagnosingMatcher { - - private final ZonedDateTime date; - - public ZonedDateTimeMatcher(ZonedDateTime date) { - this.date = date; - } - - @Override - protected boolean matchesSafely(String item, Description mismatchDescription) { - try { - if (!date.isEqual(ZonedDateTime.parse(item))) { - mismatchDescription.appendText("was ").appendValue(item); - return false; - } - return true; - } catch (DateTimeParseException e) { - mismatchDescription.appendText("was ") - .appendValue(item) - .appendText(", which could not be parsed as a ZonedDateTime"); - return false; - } - - } - - @Override - public void describeTo(Description description) { - description.appendText("a String representing the same Instant as ").appendValue(date); - } - } - - /** - * Creates a matcher that matches when the examined string reprensents the same instant as the reference datetime - * - * @param date the reference datetime against which the examined string is checked - */ - public static ZonedDateTimeMatcher sameInstant(ZonedDateTime date) { - return new ZonedDateTimeMatcher(date); - } - - /** - * Verifies the equals/hashcode contract on the domain object. - */ - public static void equalsVerifier(Class clazz) throws Exception { - T domainObject1 = clazz.getConstructor().newInstance(); - assertThat(domainObject1.toString()).isNotNull(); - assertThat(domainObject1).isEqualTo(domainObject1); - assertThat(domainObject1.hashCode()).isEqualTo(domainObject1.hashCode()); - // Test with an instance of another class - Object testOtherObject = new Object(); - assertThat(domainObject1).isNotEqualTo(testOtherObject); - assertThat(domainObject1).isNotEqualTo(null); - // Test with an instance of the same class - T domainObject2 = clazz.getConstructor().newInstance(); - assertThat(domainObject1).isNotEqualTo(domainObject2); - // HashCodes are equals because the objects are not persisted yet - assertThat(domainObject1.hashCode()).isEqualTo(domainObject2.hashCode()); - } - - /** - * Create a FormattingConversionService which use ISO date format, instead of the localized one. - * - * @return the FormattingConversionService - */ - public static FormattingConversionService createFormattingConversionService() { - DefaultFormattingConversionService dfcs = new DefaultFormattingConversionService(); - DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); - registrar.setUseIsoFormat(true); - registrar.registerFormatters(dfcs); - return dfcs; - } - - private TestUtil() { - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/UserJWTControllerIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/UserJWTControllerIntTest.java deleted file mode 100644 index 43ea4434..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/UserJWTControllerIntTest.java +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.hamcrest.Matchers.isEmptyString; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.security.jwt.TokenProvider; -import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; -import org.hostsharing.hsadminng.web.rest.vm.LoginVM; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; - -/** - * Test class for the UserJWTController REST controller. - * - * @see UserJWTController - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class UserJWTControllerIntTest { - - @Autowired - private TokenProvider tokenProvider; - - @Autowired - private AuthenticationManager authenticationManager; - - @Autowired - private UserRepository userRepository; - - @Autowired - private PasswordEncoder passwordEncoder; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - private MockMvc mockMvc; - - @Before - public void setup() { - UserJWTController userJWTController = new UserJWTController(tokenProvider, authenticationManager); - this.mockMvc = MockMvcBuilders.standaloneSetup(userJWTController) - .setControllerAdvice(exceptionTranslator) - .build(); - } - - @Test - @Transactional - public void testAuthorize() throws Exception { - User user = new User(); - user.setLogin("user-jwt-controller"); - user.setEmail("user-jwt-controller@example.com"); - user.setActivated(true); - user.setPassword(passwordEncoder.encode("test")); - - userRepository.saveAndFlush(user); - - LoginVM login = new LoginVM(); - login.setUsername("user-jwt-controller"); - login.setPassword("test"); - mockMvc.perform( - post("/api/authenticate") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(login))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id_token").isString()) - .andExpect(jsonPath("$.id_token").isNotEmpty()) - .andExpect(header().string("Authorization", not(nullValue()))) - .andExpect(header().string("Authorization", not(isEmptyString()))); - } - - @Test - @Transactional - public void testAuthorizeWithRememberMe() throws Exception { - User user = new User(); - user.setLogin("user-jwt-controller-remember-me"); - user.setEmail("user-jwt-controller-remember-me@example.com"); - user.setActivated(true); - user.setPassword(passwordEncoder.encode("test")); - - userRepository.saveAndFlush(user); - - LoginVM login = new LoginVM(); - login.setUsername("user-jwt-controller-remember-me"); - login.setPassword("test"); - login.setRememberMe(true); - mockMvc.perform( - post("/api/authenticate") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(login))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id_token").isString()) - .andExpect(jsonPath("$.id_token").isNotEmpty()) - .andExpect(header().string("Authorization", not(nullValue()))) - .andExpect(header().string("Authorization", not(isEmptyString()))); - } - - @Test - @Transactional - public void testAuthorizeFails() throws Exception { - LoginVM login = new LoginVM(); - login.setUsername("wrong-user"); - login.setPassword("wrong password"); - mockMvc.perform( - post("/api/authenticate") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(login))) - .andExpect(status().isUnauthorized()) - .andExpect(jsonPath("$.id_token").doesNotExist()) - .andExpect(header().doesNotExist("Authorization")); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/UserResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/UserResourceIntTest.java deleted file mode 100644 index 07029e3a..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/UserResourceIntTest.java +++ /dev/null @@ -1,622 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.hasItems; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.Authority; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.repository.UserRepository; -import org.hostsharing.hsadminng.security.AuthoritiesConstants; -import org.hostsharing.hsadminng.service.MailService; -import org.hostsharing.hsadminng.service.UserService; -import org.hostsharing.hsadminng.service.dto.UserDTO; -import org.hostsharing.hsadminng.service.mapper.UserMapper; -import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; -import org.hostsharing.hsadminng.web.rest.vm.ManagedUserVM; - -import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cache.CacheManager; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; -import java.util.*; - -import javax.persistence.EntityManager; - -/** - * Test class for the UserResource REST controller. - * - * @see UserResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class UserResourceIntTest { - - private static final String DEFAULT_LOGIN = "johndoe"; - private static final String UPDATED_LOGIN = "jhipster"; - - private static final Long DEFAULT_ID = 1L; - - private static final String DEFAULT_PASSWORD = "passjohndoe"; - private static final String UPDATED_PASSWORD = "passjhipster"; - - private static final String DEFAULT_EMAIL = "johndoe@localhost"; - private static final String UPDATED_EMAIL = "jhipster@localhost"; - - private static final String DEFAULT_FIRSTNAME = "john"; - private static final String UPDATED_FIRSTNAME = "jhipsterFirstName"; - - private static final String DEFAULT_LASTNAME = "doe"; - private static final String UPDATED_LASTNAME = "jhipsterLastName"; - - private static final String DEFAULT_IMAGEURL = "http://placehold.it/50x50"; - private static final String UPDATED_IMAGEURL = "http://placehold.it/40x40"; - - private static final String DEFAULT_LANGKEY = "en"; - private static final String UPDATED_LANGKEY = "fr"; - - @Autowired - private UserRepository userRepository; - - @Autowired - private MailService mailService; - - @Autowired - private UserService userService; - - @Autowired - private UserMapper userMapper; - - @Autowired - private MappingJackson2HttpMessageConverter jacksonMessageConverter; - - @Autowired - private PageableHandlerMethodArgumentResolver pageableArgumentResolver; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - @Autowired - private EntityManager em; - - @Autowired - private CacheManager cacheManager; - - private MockMvc restUserMockMvc; - - private User user; - - @Before - public void setup() { - cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).clear(); - cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).clear(); - UserResource userResource = new UserResource(userService, userRepository, mailService); - - this.restUserMockMvc = MockMvcBuilders.standaloneSetup(userResource) - .setCustomArgumentResolvers(pageableArgumentResolver) - .setControllerAdvice(exceptionTranslator) - .setMessageConverters(jacksonMessageConverter) - .build(); - } - - /** - * Create a User. - * - * This is a static method, as tests for other entities might also need it, - * if they test an entity which has a required relationship to the User entity. - */ - public static User createEntity(EntityManager em) { - User user = new User(); - user.setLogin(DEFAULT_LOGIN + RandomStringUtils.randomAlphabetic(5)); - user.setPassword(RandomStringUtils.random(60)); - user.setActivated(true); - user.setEmail(RandomStringUtils.randomAlphabetic(5) + DEFAULT_EMAIL); - user.setFirstName(DEFAULT_FIRSTNAME); - user.setLastName(DEFAULT_LASTNAME); - user.setImageUrl(DEFAULT_IMAGEURL); - user.setLangKey(DEFAULT_LANGKEY); - return user; - } - - @Before - public void initTest() { - user = createEntity(em); - user.setLogin(DEFAULT_LOGIN); - user.setEmail(DEFAULT_EMAIL); - } - - @Test - @Transactional - public void createUser() throws Exception { - int databaseSizeBeforeCreate = userRepository.findAll().size(); - - // Create the User - ManagedUserVM managedUserVM = new ManagedUserVM(); - managedUserVM.setLogin(DEFAULT_LOGIN); - managedUserVM.setPassword(DEFAULT_PASSWORD); - managedUserVM.setFirstName(DEFAULT_FIRSTNAME); - managedUserVM.setLastName(DEFAULT_LASTNAME); - managedUserVM.setEmail(DEFAULT_EMAIL); - managedUserVM.setActivated(true); - managedUserVM.setImageUrl(DEFAULT_IMAGEURL); - managedUserVM.setLangKey(DEFAULT_LANGKEY); - managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - restUserMockMvc.perform( - post("/api/users") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) - .andExpect(status().isCreated()); - - // Validate the User in the database - List userList = userRepository.findAll(); - assertThat(userList).hasSize(databaseSizeBeforeCreate + 1); - User testUser = userList.get(userList.size() - 1); - assertThat(testUser.getLogin()).isEqualTo(DEFAULT_LOGIN); - assertThat(testUser.getFirstName()).isEqualTo(DEFAULT_FIRSTNAME); - assertThat(testUser.getLastName()).isEqualTo(DEFAULT_LASTNAME); - assertThat(testUser.getEmail()).isEqualTo(DEFAULT_EMAIL); - assertThat(testUser.getImageUrl()).isEqualTo(DEFAULT_IMAGEURL); - assertThat(testUser.getLangKey()).isEqualTo(DEFAULT_LANGKEY); - } - - @Test - @Transactional - public void createUserWithExistingId() throws Exception { - int databaseSizeBeforeCreate = userRepository.findAll().size(); - - ManagedUserVM managedUserVM = new ManagedUserVM(); - managedUserVM.setId(1L); - managedUserVM.setLogin(DEFAULT_LOGIN); - managedUserVM.setPassword(DEFAULT_PASSWORD); - managedUserVM.setFirstName(DEFAULT_FIRSTNAME); - managedUserVM.setLastName(DEFAULT_LASTNAME); - managedUserVM.setEmail(DEFAULT_EMAIL); - managedUserVM.setActivated(true); - managedUserVM.setImageUrl(DEFAULT_IMAGEURL); - managedUserVM.setLangKey(DEFAULT_LANGKEY); - managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - // An entity with an existing ID cannot be created, so this API call must fail - restUserMockMvc.perform( - post("/api/users") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) - .andExpect(status().isBadRequest()); - - // Validate the User in the database - List userList = userRepository.findAll(); - assertThat(userList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void createUserWithExistingLogin() throws Exception { - // Initialize the database - userRepository.saveAndFlush(user); - int databaseSizeBeforeCreate = userRepository.findAll().size(); - - ManagedUserVM managedUserVM = new ManagedUserVM(); - managedUserVM.setLogin(DEFAULT_LOGIN);// this login should already be used - managedUserVM.setPassword(DEFAULT_PASSWORD); - managedUserVM.setFirstName(DEFAULT_FIRSTNAME); - managedUserVM.setLastName(DEFAULT_LASTNAME); - managedUserVM.setEmail("anothermail@localhost"); - managedUserVM.setActivated(true); - managedUserVM.setImageUrl(DEFAULT_IMAGEURL); - managedUserVM.setLangKey(DEFAULT_LANGKEY); - managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - // Create the User - restUserMockMvc.perform( - post("/api/users") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) - .andExpect(status().isBadRequest()); - - // Validate the User in the database - List userList = userRepository.findAll(); - assertThat(userList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void createUserWithExistingEmail() throws Exception { - // Initialize the database - userRepository.saveAndFlush(user); - int databaseSizeBeforeCreate = userRepository.findAll().size(); - - ManagedUserVM managedUserVM = new ManagedUserVM(); - managedUserVM.setLogin("anotherlogin"); - managedUserVM.setPassword(DEFAULT_PASSWORD); - managedUserVM.setFirstName(DEFAULT_FIRSTNAME); - managedUserVM.setLastName(DEFAULT_LASTNAME); - managedUserVM.setEmail(DEFAULT_EMAIL);// this email should already be used - managedUserVM.setActivated(true); - managedUserVM.setImageUrl(DEFAULT_IMAGEURL); - managedUserVM.setLangKey(DEFAULT_LANGKEY); - managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - // Create the User - restUserMockMvc.perform( - post("/api/users") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) - .andExpect(status().isBadRequest()); - - // Validate the User in the database - List userList = userRepository.findAll(); - assertThat(userList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void getAllUsers() throws Exception { - // Initialize the database - userRepository.saveAndFlush(user); - - // Get all the users - restUserMockMvc.perform( - get("/api/users?sort=id,desc") - .accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].login").value(hasItem(DEFAULT_LOGIN))) - .andExpect(jsonPath("$.[*].firstName").value(hasItem(DEFAULT_FIRSTNAME))) - .andExpect(jsonPath("$.[*].lastName").value(hasItem(DEFAULT_LASTNAME))) - .andExpect(jsonPath("$.[*].email").value(hasItem(DEFAULT_EMAIL))) - .andExpect(jsonPath("$.[*].imageUrl").value(hasItem(DEFAULT_IMAGEURL))) - .andExpect(jsonPath("$.[*].langKey").value(hasItem(DEFAULT_LANGKEY))); - } - - @Test - @Transactional - public void getUser() throws Exception { - // Initialize the database - userRepository.saveAndFlush(user); - - assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin())).isNull(); - - // Get the user - restUserMockMvc.perform(get("/api/users/{login}", user.getLogin())) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.login").value(user.getLogin())) - .andExpect(jsonPath("$.firstName").value(DEFAULT_FIRSTNAME)) - .andExpect(jsonPath("$.lastName").value(DEFAULT_LASTNAME)) - .andExpect(jsonPath("$.email").value(DEFAULT_EMAIL)) - .andExpect(jsonPath("$.imageUrl").value(DEFAULT_IMAGEURL)) - .andExpect(jsonPath("$.langKey").value(DEFAULT_LANGKEY)); - - assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin())).isNotNull(); - } - - @Test - @Transactional - public void getNonExistingUser() throws Exception { - restUserMockMvc.perform(get("/api/users/unknown")) - .andExpect(status().isNotFound()); - } - - @Test - @Transactional - public void updateUser() throws Exception { - // Initialize the database - userRepository.saveAndFlush(user); - int databaseSizeBeforeUpdate = userRepository.findAll().size(); - - // Update the user - User updatedUser = userRepository.findById(user.getId()).get(); - - ManagedUserVM managedUserVM = new ManagedUserVM(); - managedUserVM.setId(updatedUser.getId()); - managedUserVM.setLogin(updatedUser.getLogin()); - managedUserVM.setPassword(UPDATED_PASSWORD); - managedUserVM.setFirstName(UPDATED_FIRSTNAME); - managedUserVM.setLastName(UPDATED_LASTNAME); - managedUserVM.setEmail(UPDATED_EMAIL); - managedUserVM.setActivated(updatedUser.getActivated()); - managedUserVM.setImageUrl(UPDATED_IMAGEURL); - managedUserVM.setLangKey(UPDATED_LANGKEY); - managedUserVM.setCreatedBy(updatedUser.getCreatedBy()); - managedUserVM.setCreatedDate(updatedUser.getCreatedDate()); - managedUserVM.setLastModifiedBy(updatedUser.getLastModifiedBy()); - managedUserVM.setLastModifiedDate(updatedUser.getLastModifiedDate()); - managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - restUserMockMvc.perform( - put("/api/users") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) - .andExpect(status().isOk()); - - // Validate the User in the database - List userList = userRepository.findAll(); - assertThat(userList).hasSize(databaseSizeBeforeUpdate); - User testUser = userList.get(userList.size() - 1); - assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME); - assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME); - assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL); - assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL); - assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY); - } - - @Test - @Transactional - public void updateUserLogin() throws Exception { - // Initialize the database - userRepository.saveAndFlush(user); - int databaseSizeBeforeUpdate = userRepository.findAll().size(); - - // Update the user - User updatedUser = userRepository.findById(user.getId()).get(); - - ManagedUserVM managedUserVM = new ManagedUserVM(); - managedUserVM.setId(updatedUser.getId()); - managedUserVM.setLogin(UPDATED_LOGIN); - managedUserVM.setPassword(UPDATED_PASSWORD); - managedUserVM.setFirstName(UPDATED_FIRSTNAME); - managedUserVM.setLastName(UPDATED_LASTNAME); - managedUserVM.setEmail(UPDATED_EMAIL); - managedUserVM.setActivated(updatedUser.getActivated()); - managedUserVM.setImageUrl(UPDATED_IMAGEURL); - managedUserVM.setLangKey(UPDATED_LANGKEY); - managedUserVM.setCreatedBy(updatedUser.getCreatedBy()); - managedUserVM.setCreatedDate(updatedUser.getCreatedDate()); - managedUserVM.setLastModifiedBy(updatedUser.getLastModifiedBy()); - managedUserVM.setLastModifiedDate(updatedUser.getLastModifiedDate()); - managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - restUserMockMvc.perform( - put("/api/users") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) - .andExpect(status().isOk()); - - // Validate the User in the database - List userList = userRepository.findAll(); - assertThat(userList).hasSize(databaseSizeBeforeUpdate); - User testUser = userList.get(userList.size() - 1); - assertThat(testUser.getLogin()).isEqualTo(UPDATED_LOGIN); - assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME); - assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME); - assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL); - assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL); - assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY); - } - - @Test - @Transactional - public void updateUserExistingEmail() throws Exception { - // Initialize the database with 2 users - userRepository.saveAndFlush(user); - - User anotherUser = new User(); - anotherUser.setLogin("jhipster"); - anotherUser.setPassword(RandomStringUtils.random(60)); - anotherUser.setActivated(true); - anotherUser.setEmail("jhipster@localhost"); - anotherUser.setFirstName("java"); - anotherUser.setLastName("hipster"); - anotherUser.setImageUrl(""); - anotherUser.setLangKey("en"); - userRepository.saveAndFlush(anotherUser); - - // Update the user - User updatedUser = userRepository.findById(user.getId()).get(); - - ManagedUserVM managedUserVM = new ManagedUserVM(); - managedUserVM.setId(updatedUser.getId()); - managedUserVM.setLogin(updatedUser.getLogin()); - managedUserVM.setPassword(updatedUser.getPassword()); - managedUserVM.setFirstName(updatedUser.getFirstName()); - managedUserVM.setLastName(updatedUser.getLastName()); - managedUserVM.setEmail("jhipster@localhost");// this email should already be used by anotherUser - managedUserVM.setActivated(updatedUser.getActivated()); - managedUserVM.setImageUrl(updatedUser.getImageUrl()); - managedUserVM.setLangKey(updatedUser.getLangKey()); - managedUserVM.setCreatedBy(updatedUser.getCreatedBy()); - managedUserVM.setCreatedDate(updatedUser.getCreatedDate()); - managedUserVM.setLastModifiedBy(updatedUser.getLastModifiedBy()); - managedUserVM.setLastModifiedDate(updatedUser.getLastModifiedDate()); - managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - restUserMockMvc.perform( - put("/api/users") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) - .andExpect(status().isBadRequest()); - } - - @Test - @Transactional - public void updateUserExistingLogin() throws Exception { - // Initialize the database - userRepository.saveAndFlush(user); - - User anotherUser = new User(); - anotherUser.setLogin("jhipster"); - anotherUser.setPassword(RandomStringUtils.random(60)); - anotherUser.setActivated(true); - anotherUser.setEmail("jhipster@localhost"); - anotherUser.setFirstName("java"); - anotherUser.setLastName("hipster"); - anotherUser.setImageUrl(""); - anotherUser.setLangKey("en"); - userRepository.saveAndFlush(anotherUser); - - // Update the user - User updatedUser = userRepository.findById(user.getId()).get(); - - ManagedUserVM managedUserVM = new ManagedUserVM(); - managedUserVM.setId(updatedUser.getId()); - managedUserVM.setLogin("jhipster");// this login should already be used by anotherUser - managedUserVM.setPassword(updatedUser.getPassword()); - managedUserVM.setFirstName(updatedUser.getFirstName()); - managedUserVM.setLastName(updatedUser.getLastName()); - managedUserVM.setEmail(updatedUser.getEmail()); - managedUserVM.setActivated(updatedUser.getActivated()); - managedUserVM.setImageUrl(updatedUser.getImageUrl()); - managedUserVM.setLangKey(updatedUser.getLangKey()); - managedUserVM.setCreatedBy(updatedUser.getCreatedBy()); - managedUserVM.setCreatedDate(updatedUser.getCreatedDate()); - managedUserVM.setLastModifiedBy(updatedUser.getLastModifiedBy()); - managedUserVM.setLastModifiedDate(updatedUser.getLastModifiedDate()); - managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - restUserMockMvc.perform( - put("/api/users") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) - .andExpect(status().isBadRequest()); - } - - @Test - @Transactional - public void deleteUser() throws Exception { - // Initialize the database - userRepository.saveAndFlush(user); - int databaseSizeBeforeDelete = userRepository.findAll().size(); - - // Delete the user - restUserMockMvc.perform( - delete("/api/users/{login}", user.getLogin()) - .accept(TestUtil.APPLICATION_JSON_UTF8)) - .andExpect(status().isOk()); - - assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin())).isNull(); - - // Validate the database is empty - List userList = userRepository.findAll(); - assertThat(userList).hasSize(databaseSizeBeforeDelete - 1); - } - - @Test - @Transactional - public void getAllAuthorities() throws Exception { - restUserMockMvc.perform( - get("/api/users/authorities") - .accept(TestUtil.APPLICATION_JSON_UTF8) - .contentType(TestUtil.APPLICATION_JSON_UTF8)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$").isArray()) - .andExpect(jsonPath("$").value(hasItems(AuthoritiesConstants.USER, AuthoritiesConstants.ADMIN))); - } - - @Test - @Transactional - public void testUserEquals() throws Exception { - TestUtil.equalsVerifier(User.class); - User user1 = new User(); - user1.setId(1L); - User user2 = new User(); - user2.setId(user1.getId()); - assertThat(user1).isEqualTo(user2); - user2.setId(2L); - assertThat(user1).isNotEqualTo(user2); - user1.setId(null); - assertThat(user1).isNotEqualTo(user2); - } - - @Test - public void testUserDTOtoUser() { - UserDTO userDTO = new UserDTO(); - userDTO.setId(DEFAULT_ID); - userDTO.setLogin(DEFAULT_LOGIN); - userDTO.setFirstName(DEFAULT_FIRSTNAME); - userDTO.setLastName(DEFAULT_LASTNAME); - userDTO.setEmail(DEFAULT_EMAIL); - userDTO.setActivated(true); - userDTO.setImageUrl(DEFAULT_IMAGEURL); - userDTO.setLangKey(DEFAULT_LANGKEY); - userDTO.setCreatedBy(DEFAULT_LOGIN); - userDTO.setLastModifiedBy(DEFAULT_LOGIN); - userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); - - User user = userMapper.userDTOToUser(userDTO); - assertThat(user.getId()).isEqualTo(DEFAULT_ID); - assertThat(user.getLogin()).isEqualTo(DEFAULT_LOGIN); - assertThat(user.getFirstName()).isEqualTo(DEFAULT_FIRSTNAME); - assertThat(user.getLastName()).isEqualTo(DEFAULT_LASTNAME); - assertThat(user.getEmail()).isEqualTo(DEFAULT_EMAIL); - assertThat(user.getActivated()).isEqualTo(true); - assertThat(user.getImageUrl()).isEqualTo(DEFAULT_IMAGEURL); - assertThat(user.getLangKey()).isEqualTo(DEFAULT_LANGKEY); - assertThat(user.getCreatedBy()).isNull(); - assertThat(user.getCreatedDate()).isNotNull(); - assertThat(user.getLastModifiedBy()).isNull(); - assertThat(user.getLastModifiedDate()).isNotNull(); - assertThat(user.getAuthorities()).extracting("name").containsExactly(AuthoritiesConstants.USER); - } - - @Test - public void testUserToUserDTO() { - user.setId(DEFAULT_ID); - user.setCreatedBy(DEFAULT_LOGIN); - user.setCreatedDate(Instant.now()); - user.setLastModifiedBy(DEFAULT_LOGIN); - user.setLastModifiedDate(Instant.now()); - Set authorities = new HashSet<>(); - Authority authority = new Authority(); - authority.setName(AuthoritiesConstants.USER); - authorities.add(authority); - user.setAuthorities(authorities); - - UserDTO userDTO = userMapper.userToUserDTO(user); - - assertThat(userDTO.getId()).isEqualTo(DEFAULT_ID); - assertThat(userDTO.getLogin()).isEqualTo(DEFAULT_LOGIN); - assertThat(userDTO.getFirstName()).isEqualTo(DEFAULT_FIRSTNAME); - assertThat(userDTO.getLastName()).isEqualTo(DEFAULT_LASTNAME); - assertThat(userDTO.getEmail()).isEqualTo(DEFAULT_EMAIL); - assertThat(userDTO.isActivated()).isEqualTo(true); - assertThat(userDTO.getImageUrl()).isEqualTo(DEFAULT_IMAGEURL); - assertThat(userDTO.getLangKey()).isEqualTo(DEFAULT_LANGKEY); - assertThat(userDTO.getCreatedBy()).isEqualTo(DEFAULT_LOGIN); - assertThat(userDTO.getCreatedDate()).isEqualTo(user.getCreatedDate()); - assertThat(userDTO.getLastModifiedBy()).isEqualTo(DEFAULT_LOGIN); - assertThat(userDTO.getLastModifiedDate()).isEqualTo(user.getLastModifiedDate()); - assertThat(userDTO.getAuthorities()).containsExactly(AuthoritiesConstants.USER); - assertThat(userDTO.toString()).isNotNull(); - } - - @Test - public void testAuthorityEquals() { - Authority authorityA = new Authority(); - assertThat(authorityA).isEqualTo(authorityA); - assertThat(authorityA).isNotEqualTo(null); - assertThat(authorityA).isNotEqualTo(new Object()); - assertThat(authorityA.hashCode()).isEqualTo(0); - assertThat(authorityA.toString()).isNotNull(); - - Authority authorityB = new Authority(); - assertThat(authorityA).isEqualTo(authorityB); - - authorityB.setName(AuthoritiesConstants.ADMIN); - assertThat(authorityA).isNotEqualTo(authorityB); - - authorityA.setName(AuthoritiesConstants.USER); - assertThat(authorityA).isNotEqualTo(authorityB); - - authorityB.setName(AuthoritiesConstants.USER); - assertThat(authorityA).isEqualTo(authorityB); - assertThat(authorityA.hashCode()).isEqualTo(authorityB.hashCode()); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResourceIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResourceIntTest.java deleted file mode 100644 index cfadadd8..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResourceIntTest.java +++ /dev/null @@ -1,552 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.hasItem; -import static org.hostsharing.hsadminng.web.rest.TestUtil.createFormattingConversionService; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.hostsharing.hsadminng.HsadminNgApp; -import org.hostsharing.hsadminng.domain.User; -import org.hostsharing.hsadminng.domain.UserRoleAssignment; -import org.hostsharing.hsadminng.repository.UserRoleAssignmentRepository; -import org.hostsharing.hsadminng.service.UserRoleAssignmentQueryService; -import org.hostsharing.hsadminng.service.UserRoleAssignmentService; -import org.hostsharing.hsadminng.service.accessfilter.Role; -import org.hostsharing.hsadminng.service.accessfilter.Role.Admin; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerContractualContact; -import org.hostsharing.hsadminng.service.accessfilter.Role.CustomerTechnicalContact; -import org.hostsharing.hsadminng.service.accessfilter.SecurityContextFake; -import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.Validator; - -import java.util.List; - -import javax.persistence.EntityManager; - -/** - * Test class for the UserRoleAssignmentResource REST controller. - * - * @see UserRoleAssignmentResource - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = { HsadminNgApp.class }) -public class UserRoleAssignmentResourceIntTest { - - private static final String DEFAULT_ENTITY_TYPE_ID = "AAAAAAAAAA"; - private static final String UPDATED_ENTITY_TYPE_ID = "BBBBBBBBBB"; - - private static final Long DEFAULT_ENTITY_OBJECT_ID = 1L; - private static final Long UPDATED_ENTITY_OBJECT_ID = 2L; - - private static final Role DEFAULT_ASSIGNED_ROLE = CustomerTechnicalContact.ROLE; - private static final Role UPDATED_ASSIGNED_ROLE = CustomerContractualContact.ROLE; - - @Autowired - private UserRoleAssignmentRepository userRoleAssignmentRepository; - - @Autowired - private UserRoleAssignmentService userRoleAssignmentService; - - @Autowired - private UserRoleAssignmentQueryService userRoleAssignmentQueryService; - - @Autowired - private MappingJackson2HttpMessageConverter jacksonMessageConverter; - - @Autowired - private PageableHandlerMethodArgumentResolver pageableArgumentResolver; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - @Autowired - private EntityManager em; - - @Autowired - private Validator validator; - - private MockMvc restUserRoleAssignmentMockMvc; - - private UserRoleAssignment userRoleAssignment; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - final UserRoleAssignmentResource userRoleAssignmentResource = new UserRoleAssignmentResource( - userRoleAssignmentService, - userRoleAssignmentQueryService); - this.restUserRoleAssignmentMockMvc = MockMvcBuilders.standaloneSetup(userRoleAssignmentResource) - .setCustomArgumentResolvers(pageableArgumentResolver) - .setControllerAdvice(exceptionTranslator) - .setConversionService(createFormattingConversionService()) - .setMessageConverters(jacksonMessageConverter) - .setValidator(validator) - .build(); - - SecurityContextFake.havingAuthenticatedUser().withAuthority(Role.Supporter.ROLE.authority()); - } - - /** - * Create an entity for this test. - * - * This is a static method, as tests for other entities might also need it, - * if they test an entity which requires the current entity. - */ - public static UserRoleAssignment createEntity(EntityManager em) { - User user = UserResourceIntTest.createEntity(em); - em.persist(user); - em.flush(); - return new UserRoleAssignment() - .entityTypeId(DEFAULT_ENTITY_TYPE_ID) - .entityObjectId(DEFAULT_ENTITY_OBJECT_ID) - .user(user) - .assignedRole(DEFAULT_ASSIGNED_ROLE); - } - - @Before - public void initTest() { - userRoleAssignment = createEntity(em); - } - - @Test - @Transactional - public void createUserRoleAssignment() throws Exception { - int databaseSizeBeforeCreate = userRoleAssignmentRepository.findAll().size(); - - // Create the UserRoleAssignment - SecurityContextFake.havingAuthenticatedUser().withAuthority(Admin.ROLE.authority()); - restUserRoleAssignmentMockMvc.perform( - post("/api/user-role-assignments") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userRoleAssignment))) - .andExpect(status().isCreated()); - - // Validate the UserRoleAssignment in the database - List userRoleAssignmentList = userRoleAssignmentRepository.findAll(); - assertThat(userRoleAssignmentList).hasSize(databaseSizeBeforeCreate + 1); - UserRoleAssignment testUserRoleAssignment = userRoleAssignmentList.get(userRoleAssignmentList.size() - 1); - assertThat(testUserRoleAssignment.getEntityTypeId()).isEqualTo(DEFAULT_ENTITY_TYPE_ID); - assertThat(testUserRoleAssignment.getEntityObjectId()).isEqualTo(DEFAULT_ENTITY_OBJECT_ID); - assertThat(testUserRoleAssignment.getAssignedRole().name()).isEqualTo(DEFAULT_ASSIGNED_ROLE.name()); - assertThat(testUserRoleAssignment.getAssignedRole()).isEqualTo(DEFAULT_ASSIGNED_ROLE); - } - - @Test - @Transactional - public void createUserRoleAssignmentWithExistingId() throws Exception { - int databaseSizeBeforeCreate = userRoleAssignmentRepository.findAll().size(); - - // Create the UserRoleAssignment with an existing ID - userRoleAssignment.setId(1L); - - // An entity with an existing ID cannot be created, so this API call must fail - restUserRoleAssignmentMockMvc.perform( - post("/api/user-role-assignments") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userRoleAssignment))) - .andExpect(status().isBadRequest()); - - // Validate the UserRoleAssignment in the database - List userRoleAssignmentList = userRoleAssignmentRepository.findAll(); - assertThat(userRoleAssignmentList).hasSize(databaseSizeBeforeCreate); - } - - @Test - @Transactional - public void checkEntityTypeIdIsRequired() throws Exception { - int databaseSizeBeforeTest = userRoleAssignmentRepository.findAll().size(); - // set the field null - userRoleAssignment.setEntityTypeId(null); - - // Create the UserRoleAssignment, which fails. - - restUserRoleAssignmentMockMvc.perform( - post("/api/user-role-assignments") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userRoleAssignment))) - .andExpect(status().isBadRequest()); - - List userRoleAssignmentList = userRoleAssignmentRepository.findAll(); - assertThat(userRoleAssignmentList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkEntityObjectIdIsRequired() throws Exception { - int databaseSizeBeforeTest = userRoleAssignmentRepository.findAll().size(); - // set the field null - userRoleAssignment.setEntityObjectId(null); - - // Create the UserRoleAssignment, which fails. - - restUserRoleAssignmentMockMvc.perform( - post("/api/user-role-assignments") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userRoleAssignment))) - .andExpect(status().isBadRequest()); - - List userRoleAssignmentList = userRoleAssignmentRepository.findAll(); - assertThat(userRoleAssignmentList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void checkAssignedRoleIsRequired() throws Exception { - int databaseSizeBeforeTest = userRoleAssignmentRepository.findAll().size(); - // set the field null - userRoleAssignment.setAssignedRole(null); - - // Create the UserRoleAssignment, which fails. - - restUserRoleAssignmentMockMvc.perform( - post("/api/user-role-assignments") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userRoleAssignment))) - .andExpect(status().isBadRequest()); - - List userRoleAssignmentList = userRoleAssignmentRepository.findAll(); - assertThat(userRoleAssignmentList).hasSize(databaseSizeBeforeTest); - } - - @Test - @Transactional - public void getAllUserRoleAssignments() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList - restUserRoleAssignmentMockMvc.perform(get("/api/user-role-assignments?sort=id,desc")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(userRoleAssignment.getId().intValue()))) - .andExpect(jsonPath("$.[*].entityTypeId").value(hasItem(DEFAULT_ENTITY_TYPE_ID))) - .andExpect(jsonPath("$.[*].entityObjectId").value(hasItem(DEFAULT_ENTITY_OBJECT_ID.intValue()))) - .andExpect(jsonPath("$.[*].assignedRole").value(hasItem(DEFAULT_ASSIGNED_ROLE.name()))); - } - - @Test - @Transactional - public void getUserRoleAssignment() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get the userRoleAssignment - restUserRoleAssignmentMockMvc.perform(get("/api/user-role-assignments/{id}", userRoleAssignment.getId())) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.id").value(userRoleAssignment.getId().intValue())) - .andExpect(jsonPath("$.entityTypeId").value(DEFAULT_ENTITY_TYPE_ID)) - .andExpect(jsonPath("$.entityObjectId").value(DEFAULT_ENTITY_OBJECT_ID.intValue())) - .andExpect(jsonPath("$.assignedRole").value(DEFAULT_ASSIGNED_ROLE.name())); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByEntityTypeIdIsEqualToSomething() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where entityTypeId equals to DEFAULT_ENTITY_TYPE_ID - defaultUserRoleAssignmentShouldBeFound("entityTypeId.equals=" + DEFAULT_ENTITY_TYPE_ID); - - // Get all the userRoleAssignmentList where entityTypeId equals to UPDATED_ENTITY_TYPE_ID - defaultUserRoleAssignmentShouldNotBeFound("entityTypeId.equals=" + UPDATED_ENTITY_TYPE_ID); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByEntityTypeIdIsInShouldWork() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where entityTypeId in DEFAULT_ENTITY_TYPE_ID or UPDATED_ENTITY_TYPE_ID - defaultUserRoleAssignmentShouldBeFound("entityTypeId.in=" + DEFAULT_ENTITY_TYPE_ID + "," + UPDATED_ENTITY_TYPE_ID); - - // Get all the userRoleAssignmentList where entityTypeId equals to UPDATED_ENTITY_TYPE_ID - defaultUserRoleAssignmentShouldNotBeFound("entityTypeId.in=" + UPDATED_ENTITY_TYPE_ID); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByEntityTypeIdIsNullOrNotNull() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where entityTypeId is not null - defaultUserRoleAssignmentShouldBeFound("entityTypeId.specified=true"); - - // Get all the userRoleAssignmentList where entityTypeId is null - defaultUserRoleAssignmentShouldNotBeFound("entityTypeId.specified=false"); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByEntityObjectIdIsEqualToSomething() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where entityObjectId equals to DEFAULT_ENTITY_OBJECT_ID - defaultUserRoleAssignmentShouldBeFound("entityObjectId.equals=" + DEFAULT_ENTITY_OBJECT_ID); - - // Get all the userRoleAssignmentList where entityObjectId equals to UPDATED_ENTITY_OBJECT_ID - defaultUserRoleAssignmentShouldNotBeFound("entityObjectId.equals=" + UPDATED_ENTITY_OBJECT_ID); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByEntityObjectIdIsInShouldWork() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where entityObjectId in DEFAULT_ENTITY_OBJECT_ID or UPDATED_ENTITY_OBJECT_ID - defaultUserRoleAssignmentShouldBeFound( - "entityObjectId.in=" + DEFAULT_ENTITY_OBJECT_ID + "," + UPDATED_ENTITY_OBJECT_ID); - - // Get all the userRoleAssignmentList where entityObjectId equals to UPDATED_ENTITY_OBJECT_ID - defaultUserRoleAssignmentShouldNotBeFound("entityObjectId.in=" + UPDATED_ENTITY_OBJECT_ID); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByEntityObjectIdIsNullOrNotNull() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where entityObjectId is not null - defaultUserRoleAssignmentShouldBeFound("entityObjectId.specified=true"); - - // Get all the userRoleAssignmentList where entityObjectId is null - defaultUserRoleAssignmentShouldNotBeFound("entityObjectId.specified=false"); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByEntityObjectIdIsGreaterThanOrEqualToSomething() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where entityObjectId greater than or equals to DEFAULT_ENTITY_OBJECT_ID - defaultUserRoleAssignmentShouldBeFound("entityObjectId.greaterOrEqualThan=" + DEFAULT_ENTITY_OBJECT_ID); - - // Get all the userRoleAssignmentList where entityObjectId greater than or equals to UPDATED_ENTITY_OBJECT_ID - defaultUserRoleAssignmentShouldNotBeFound("entityObjectId.greaterOrEqualThan=" + UPDATED_ENTITY_OBJECT_ID); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByEntityObjectIdIsLessThanSomething() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where entityObjectId less than or equals to DEFAULT_ENTITY_OBJECT_ID - defaultUserRoleAssignmentShouldNotBeFound("entityObjectId.lessThan=" + DEFAULT_ENTITY_OBJECT_ID); - - // Get all the userRoleAssignmentList where entityObjectId less than or equals to UPDATED_ENTITY_OBJECT_ID - defaultUserRoleAssignmentShouldBeFound("entityObjectId.lessThan=" + UPDATED_ENTITY_OBJECT_ID); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByAssignedRoleIsEqualToSomething() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where assignedRole equals to DEFAULT_ASSIGNED_ROLE - defaultUserRoleAssignmentShouldBeFound("assignedRole.equals=" + DEFAULT_ASSIGNED_ROLE.name()); - - // Get all the userRoleAssignmentList where assignedRole equals to UPDATED_ASSIGNED_ROLE - defaultUserRoleAssignmentShouldNotBeFound("assignedRole.equals=" + UPDATED_ASSIGNED_ROLE.name()); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByAssignedRoleIsInShouldWork() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where assignedRole in DEFAULT_ASSIGNED_ROLE or UPDATED_ASSIGNED_ROLE - defaultUserRoleAssignmentShouldBeFound( - "assignedRole.in=" + DEFAULT_ASSIGNED_ROLE.name() + "," + UPDATED_ASSIGNED_ROLE.name()); - - // Get all the userRoleAssignmentList where assignedRole equals to UPDATED_ASSIGNED_ROLE - defaultUserRoleAssignmentShouldNotBeFound("assignedRole.in=" + UPDATED_ASSIGNED_ROLE.name()); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByAssignedRoleIsNullOrNotNull() throws Exception { - // Initialize the database - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - - // Get all the userRoleAssignmentList where assignedRole is not null - defaultUserRoleAssignmentShouldBeFound("assignedRole.specified=true"); - - // Get all the userRoleAssignmentList where assignedRole is null - defaultUserRoleAssignmentShouldNotBeFound("assignedRole.specified=false"); - } - - @Test - @Transactional - public void getAllUserRoleAssignmentsByUserIsEqualToSomething() throws Exception { - // Initialize the database - User user = UserResourceIntTest.createEntity(em); - em.persist(user); - em.flush(); - userRoleAssignment.setUser(user); - userRoleAssignmentRepository.saveAndFlush(userRoleAssignment); - Long userId = user.getId(); - - // Get all the userRoleAssignmentList where user equals to userId - defaultUserRoleAssignmentShouldBeFound("userId.equals=" + userId); - - // Get all the userRoleAssignmentList where user equals to userId + 1 - defaultUserRoleAssignmentShouldNotBeFound("userId.equals=" + (userId + 1)); - } - - /** - * Executes the search, and checks that the default entity is returned - */ - private void defaultUserRoleAssignmentShouldBeFound(String filter) throws Exception { - restUserRoleAssignmentMockMvc.perform(get("/api/user-role-assignments?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$.[*].id").value(hasItem(userRoleAssignment.getId().intValue()))) - .andExpect(jsonPath("$.[*].entityTypeId").value(hasItem(DEFAULT_ENTITY_TYPE_ID))) - .andExpect(jsonPath("$.[*].entityObjectId").value(hasItem(DEFAULT_ENTITY_OBJECT_ID.intValue()))) - .andExpect(jsonPath("$.[*].assignedRole").value(hasItem(DEFAULT_ASSIGNED_ROLE.name()))); - - // Check, that the count call also returns 1 - restUserRoleAssignmentMockMvc.perform(get("/api/user-role-assignments/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("1")); - } - - /** - * Executes the search, and checks that the default entity is not returned - */ - private void defaultUserRoleAssignmentShouldNotBeFound(String filter) throws Exception { - restUserRoleAssignmentMockMvc.perform(get("/api/user-role-assignments?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$").isArray()) - .andExpect(jsonPath("$").isEmpty()); - - // Check, that the count call also returns 0 - restUserRoleAssignmentMockMvc.perform(get("/api/user-role-assignments/count?sort=id,desc&" + filter)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) - .andExpect(content().string("0")); - } - - @Test - @Transactional - public void getNonExistingUserRoleAssignment() throws Exception { - // Get the userRoleAssignment - restUserRoleAssignmentMockMvc.perform(get("/api/user-role-assignments/{id}", Long.MAX_VALUE)) - .andExpect(status().isNotFound()); - } - - @Test - @Transactional - public void updateUserRoleAssignment() throws Exception { - // Initialize the database - userRoleAssignmentService.save(userRoleAssignment); - - int databaseSizeBeforeUpdate = userRoleAssignmentRepository.findAll().size(); - - // Update the userRoleAssignment - SecurityContextFake.havingAuthenticatedUser().withAuthority(Admin.ROLE.authority()); - UserRoleAssignment updatedUserRoleAssignment = userRoleAssignmentRepository.findById(userRoleAssignment.getId()).get(); - // Disconnect from session so that the updates on updatedUserRoleAssignment are not directly saved in db - em.detach(updatedUserRoleAssignment); - updatedUserRoleAssignment - .entityTypeId(UPDATED_ENTITY_TYPE_ID) - .entityObjectId(UPDATED_ENTITY_OBJECT_ID) - .assignedRole(UPDATED_ASSIGNED_ROLE); - - restUserRoleAssignmentMockMvc.perform( - put("/api/user-role-assignments") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(updatedUserRoleAssignment))) - .andExpect(status().isOk()); - - // Validate the UserRoleAssignment in the database - List userRoleAssignmentList = userRoleAssignmentRepository.findAll(); - assertThat(userRoleAssignmentList).hasSize(databaseSizeBeforeUpdate); - UserRoleAssignment testUserRoleAssignment = userRoleAssignmentList.get(userRoleAssignmentList.size() - 1); - assertThat(testUserRoleAssignment.getEntityTypeId()).isEqualTo(UPDATED_ENTITY_TYPE_ID); - assertThat(testUserRoleAssignment.getEntityObjectId()).isEqualTo(UPDATED_ENTITY_OBJECT_ID); - assertThat(testUserRoleAssignment.getAssignedRole().name()).isEqualTo(UPDATED_ASSIGNED_ROLE.name()); - assertThat(testUserRoleAssignment.getAssignedRole()).isEqualTo(UPDATED_ASSIGNED_ROLE); - } - - @Test - @Transactional - public void updateNonExistingUserRoleAssignment() throws Exception { - int databaseSizeBeforeUpdate = userRoleAssignmentRepository.findAll().size(); - - // Create the UserRoleAssignment - - // If the entity doesn't have an ID, it will throw BadRequestAlertException - restUserRoleAssignmentMockMvc.perform( - put("/api/user-role-assignments") - .contentType(TestUtil.APPLICATION_JSON_UTF8) - .content(TestUtil.convertObjectToJsonBytes(userRoleAssignment))) - .andExpect(status().isBadRequest()); - - // Validate the UserRoleAssignment in the database - List userRoleAssignmentList = userRoleAssignmentRepository.findAll(); - assertThat(userRoleAssignmentList).hasSize(databaseSizeBeforeUpdate); - } - - @Test - @Transactional - public void deleteUserRoleAssignment() throws Exception { - // Initialize the database - userRoleAssignmentService.save(userRoleAssignment); - - int databaseSizeBeforeDelete = userRoleAssignmentRepository.findAll().size(); - - // Delete the userRoleAssignment - restUserRoleAssignmentMockMvc.perform( - delete("/api/user-role-assignments/{id}", userRoleAssignment.getId()) - .accept(TestUtil.APPLICATION_JSON_UTF8)) - .andExpect(status().isOk()); - - // Validate the database is empty - List userRoleAssignmentList = userRoleAssignmentRepository.findAll(); - assertThat(userRoleAssignmentList).hasSize(databaseSizeBeforeDelete - 1); - } - - @Test - @Transactional - public void equalsVerifier() throws Exception { - TestUtil.equalsVerifier(UserRoleAssignment.class); - UserRoleAssignment userRoleAssignment1 = new UserRoleAssignment(); - userRoleAssignment1.setId(1L); - UserRoleAssignment userRoleAssignment2 = new UserRoleAssignment(); - userRoleAssignment2.setId(userRoleAssignment1.getId()); - assertThat(userRoleAssignment1).isEqualTo(userRoleAssignment2); - userRoleAssignment2.setId(2L); - assertThat(userRoleAssignment1).isNotEqualTo(userRoleAssignment2); - userRoleAssignment1.setId(null); - assertThat(userRoleAssignment1).isNotEqualTo(userRoleAssignment2); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResourceUnitTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResourceUnitTest.java deleted file mode 100644 index d5774a42..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/UserRoleAssignmentResourceUnitTest.java +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.ThrowableAssert.catchThrowable; - -import org.hostsharing.hsadminng.domain.UserRoleAssignment; -import org.hostsharing.hsadminng.service.dto.UserRoleAssignmentUnitTest; -import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; - -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -// Currently this class tests mostly special 'bad paths' -// which make little sense to test in *ResourceIntTest. -public class UserRoleAssignmentResourceUnitTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @InjectMocks - private UserRoleAssignmentResource userRoleAssignmentResource; - - @Test - public void createUserRoleAssignmentWithoutIdThrowsBadRequestException() { - - // given - final UserRoleAssignment givenEntity = UserRoleAssignmentUnitTest.createSomeUserRoleAssignment(null); - - // when - final Throwable actual = catchThrowable(() -> userRoleAssignmentResource.updateUserRoleAssignment(givenEntity)); - - // then - assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> { - assertThat(bre.getErrorKey()).isEqualTo("idnull"); - assertThat(bre.getParam()).isEqualTo("userRoleAssignment"); - }); - } - - @Test - public void createUserRoleAssignmentWithIdThrowsBadRequestException() { - - // given - final UserRoleAssignment givenEntity = UserRoleAssignmentUnitTest.createSomeUserRoleAssignment(1L); - - // when - final Throwable actual = catchThrowable(() -> userRoleAssignmentResource.createUserRoleAssignment(givenEntity)); - - // then - assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> { - assertThat(bre.getErrorKey()).isEqualTo("idexists"); - assertThat(bre.getParam()).isEqualTo("userRoleAssignment"); - }); - } -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslatorIntTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslatorIntTest.java deleted file mode 100644 index d99a9669..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslatorIntTest.java +++ /dev/null @@ -1,152 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import org.hostsharing.hsadminng.HsadminNgApp; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -/** - * Test class for the ExceptionTranslator controller advice. - * - * @see ExceptionTranslator - */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = HsadminNgApp.class) -public class ExceptionTranslatorIntTest { - - @Autowired - private ExceptionTranslatorTestController controller; - - @Autowired - private ExceptionTranslator exceptionTranslator; - - @Autowired - private MappingJackson2HttpMessageConverter jacksonMessageConverter; - - private MockMvc mockMvc; - - @Before - public void setup() { - mockMvc = MockMvcBuilders.standaloneSetup(controller) - .setControllerAdvice(exceptionTranslator) - .setMessageConverters(jacksonMessageConverter) - .build(); - } - - @Test - public void testConcurrencyFailure() throws Exception { - mockMvc.perform(get("/test/concurrency-failure")) - .andExpect(status().isConflict()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value(ErrorConstants.ERR_CONCURRENCY_FAILURE)); - } - - @Test - public void testMethodArgumentNotValid() throws Exception { - mockMvc.perform(post("/test/method-argument").content("{}").contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value(ErrorConstants.ERR_VALIDATION)) - .andExpect(jsonPath("$.fieldErrors.[0].objectName").value("testDTO")) - .andExpect(jsonPath("$.fieldErrors.[0].field").value("test")) - .andExpect(jsonPath("$.fieldErrors.[0].message").value("NotNull")); - } - - @Test - public void testParameterizedError() throws Exception { - mockMvc.perform(get("/test/parameterized-error")) - .andExpect(status().isBadRequest()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value("test parameterized error")) - .andExpect(jsonPath("$.params.param0").value("param0_value")) - .andExpect(jsonPath("$.params.param1").value("param1_value")); - } - - @Test - public void testParameterizedError2() throws Exception { - mockMvc.perform(get("/test/parameterized-error2")) - .andExpect(status().isBadRequest()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value("test parameterized error")) - .andExpect(jsonPath("$.params.foo").value("foo_value")) - .andExpect(jsonPath("$.params.bar").value("bar_value")); - } - - @Test - public void testMissingServletRequestPartException() throws Exception { - mockMvc.perform(get("/test/missing-servlet-request-part")) - .andExpect(status().isBadRequest()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value("error.http.400")); - } - - @Test - public void testMissingServletRequestParameterException() throws Exception { - mockMvc.perform(get("/test/missing-servlet-request-parameter")) - .andExpect(status().isBadRequest()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value("error.http.400")); - } - - @Test - public void testAccessDenied() throws Exception { - mockMvc.perform(get("/test/access-denied")) - .andExpect(status().isForbidden()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value("error.http.403")) - .andExpect(jsonPath("$.detail").value("test access denied!")); - } - - @Test - public void testUnauthorized() throws Exception { - mockMvc.perform(get("/test/unauthorized")) - .andExpect(status().isUnauthorized()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value("error.http.401")) - .andExpect(jsonPath("$.path").value("/test/unauthorized")) - .andExpect(jsonPath("$.detail").value("test authentication failed!")); - } - - @Test - public void testMethodNotSupported() throws Exception { - mockMvc.perform(post("/test/access-denied")) - .andExpect(status().isMethodNotAllowed()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value("error.http.405")) - .andExpect(jsonPath("$.detail").value("Request method 'POST' not supported")); - } - - @Test - public void testExceptionWithResponseStatus() throws Exception { - mockMvc.perform(get("/test/response-status")) - .andExpect(status().isBadRequest()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value("error.http.400")) - .andExpect(jsonPath("$.title").value("test response status")); - } - - @Test - public void testInternalServerError() throws Exception { - mockMvc.perform(get("/test/internal-server-error")) - .andExpect(status().isInternalServerError()) - .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) - .andExpect(jsonPath("$.message").value("error.http.500")) - .andExpect(jsonPath("$.title").value("Internal Server Error")); - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslatorTestController.java b/src/test/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslatorTestController.java deleted file mode 100644 index 4465a55a..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/errors/ExceptionTranslatorTestController.java +++ /dev/null @@ -1,88 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.errors; - -import org.springframework.dao.ConcurrencyFailureException; -import org.springframework.http.HttpStatus; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.web.bind.annotation.*; - -import java.util.HashMap; -import java.util.Map; - -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - -@RestController -public class ExceptionTranslatorTestController { - - @GetMapping("/test/concurrency-failure") - public void concurrencyFailure() { - throw new ConcurrencyFailureException("test concurrency failure"); - } - - @PostMapping("/test/method-argument") - public void methodArgument(@Valid @RequestBody TestDTO testDTO) { - } - - @GetMapping("/test/parameterized-error") - public void parameterizedError() { - throw new CustomParameterizedException("test parameterized error", "param0_value", "param1_value"); - } - - @GetMapping("/test/parameterized-error2") - public void parameterizedError2() { - Map params = new HashMap<>(); - params.put("foo", "foo_value"); - params.put("bar", "bar_value"); - throw new CustomParameterizedException("test parameterized error", params); - } - - @GetMapping("/test/missing-servlet-request-part") - public void missingServletRequestPartException(@RequestPart String part) { - } - - @GetMapping("/test/missing-servlet-request-parameter") - public void missingServletRequestParameterException(@RequestParam String param) { - } - - @GetMapping("/test/access-denied") - public void accessdenied() { - throw new AccessDeniedException("test access denied!"); - } - - @GetMapping("/test/unauthorized") - public void unauthorized() { - throw new BadCredentialsException("test authentication failed!"); - } - - @GetMapping("/test/response-status") - public void exceptionWithReponseStatus() { - throw new TestResponseStatusException(); - } - - @GetMapping("/test/internal-server-error") - public void internalServerError() { - throw new RuntimeException(); - } - - public static class TestDTO { - - @NotNull - private String test; - - public String getTest() { - return test; - } - - public void setTest(String test) { - this.test = test; - } - } - - @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "test response status") - @SuppressWarnings("serial") - public static class TestResponseStatusException extends RuntimeException { - } - -} diff --git a/src/test/java/org/hostsharing/hsadminng/web/rest/util/PaginationUtilUnitTest.java b/src/test/java/org/hostsharing/hsadminng/web/rest/util/PaginationUtilUnitTest.java deleted file mode 100644 index 6cabf756..00000000 --- a/src/test/java/org/hostsharing/hsadminng/web/rest/util/PaginationUtilUnitTest.java +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed under Apache-2.0 -package org.hostsharing.hsadminng.web.rest.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.http.HttpHeaders; - -import java.util.ArrayList; -import java.util.List; - -/** - * Tests based on parsing algorithm in app/components/util/pagination-util.service.js - * - * @see PaginationUtil - */ -public class PaginationUtilUnitTest { - - @Test - public void generatePaginationHttpHeadersTest() { - String baseUrl = "/api/_search/example"; - List content = new ArrayList<>(); - Page page = new PageImpl<>(content, PageRequest.of(6, 50), 400L); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, baseUrl); - List strHeaders = headers.get(HttpHeaders.LINK); - assertNotNull(strHeaders); - assertTrue(strHeaders.size() == 1); - String headerData = strHeaders.get(0); - assertTrue(headerData.split(",").length == 4); - String expectedData = "; rel=\"next\"," - + "; rel=\"prev\"," - + "; rel=\"last\"," - + "; rel=\"first\""; - assertEquals(expectedData, headerData); - List xTotalCountHeaders = headers.get("X-Total-Count"); - assertTrue(xTotalCountHeaders.size() == 1); - assertTrue(Long.valueOf(xTotalCountHeaders.get(0)).equals(400L)); - } - -} diff --git a/src/test/javascript/jest-global-mocks.ts b/src/test/javascript/jest-global-mocks.ts deleted file mode 100644 index a9982598..00000000 --- a/src/test/javascript/jest-global-mocks.ts +++ /dev/null @@ -1,15 +0,0 @@ -const mock = () => { - let storage = {}; - return { - getItem: key => (key in storage ? storage[key] : null), - setItem: (key, value) => (storage[key] = value || ''), - removeItem: key => delete storage[key], - clear: () => (storage = {}) - }; -}; - -Object.defineProperty(window, 'localStorage', { value: mock() }); -Object.defineProperty(window, 'sessionStorage', { value: mock() }); -Object.defineProperty(window, 'getComputedStyle', { - value: () => ['-webkit-appearance'] -}); diff --git a/src/test/javascript/jest.conf.js b/src/test/javascript/jest.conf.js deleted file mode 100644 index 71f72625..00000000 --- a/src/test/javascript/jest.conf.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - preset: 'jest-preset-angular', - setupTestFrameworkScriptFile: '/src/test/javascript/jest.ts', - coverageDirectory: '/build/test-results/', - globals: { - 'ts-jest': { - tsConfigFile: 'tsconfig.json' - }, - __TRANSFORM_HTML__: true - }, - coveragePathIgnorePatterns: [ - '/src/test/javascript' - ], - moduleNameMapper: { - 'app/(.*)': '/src/main/webapp/app/$1' - }, - reporters: [ - 'default', - [ 'jest-junit', { output: './build/test-results/TESTS-results-jest.xml' } ] - ], - testResultsProcessor: 'jest-sonar-reporter', - transformIgnorePatterns: ['node_modules/(?!@angular/common/locales)'], - testMatch: ['/src/test/javascript/spec/**/+(*.)+(spec.ts)'], - rootDir: '../../../', - testURL: "http://localhost/" -}; diff --git a/src/test/javascript/jest.ts b/src/test/javascript/jest.ts deleted file mode 100644 index 904329f5..00000000 --- a/src/test/javascript/jest.ts +++ /dev/null @@ -1,2 +0,0 @@ -import 'jest-preset-angular'; -import './jest-global-mocks'; diff --git a/src/test/javascript/spec/app/account/activate/activate.component.spec.ts b/src/test/javascript/spec/app/account/activate/activate.component.spec.ts deleted file mode 100644 index f8714911..00000000 --- a/src/test/javascript/spec/app/account/activate/activate.component.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { TestBed, async, tick, fakeAsync, inject } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { Observable, of, throwError } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { MockActivatedRoute } from '../../../helpers/mock-route.service'; -import { ActivateService } from 'app/account/activate/activate.service'; -import { ActivateComponent } from 'app/account/activate/activate.component'; - -describe('Component Tests', () => { - describe('ActivateComponent', () => { - let comp: ActivateComponent; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [ActivateComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: new MockActivatedRoute({ key: 'ABC123' }) - } - ] - }) - .overrideTemplate(ActivateComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - const fixture = TestBed.createComponent(ActivateComponent); - comp = fixture.componentInstance; - }); - - it('calls activate.get with the key from params', inject( - [ActivateService], - fakeAsync((service: ActivateService) => { - spyOn(service, 'get').and.returnValue(of()); - - comp.ngOnInit(); - tick(); - - expect(service.get).toHaveBeenCalledWith('ABC123'); - }) - )); - - it('should set set success to OK upon successful activation', inject( - [ActivateService], - fakeAsync((service: ActivateService) => { - spyOn(service, 'get').and.returnValue(of({})); - - comp.ngOnInit(); - tick(); - - expect(comp.error).toBe(null); - expect(comp.success).toEqual('OK'); - }) - )); - - it('should set set error to ERROR upon activation failure', inject( - [ActivateService], - fakeAsync((service: ActivateService) => { - spyOn(service, 'get').and.returnValue(throwError('ERROR')); - - comp.ngOnInit(); - tick(); - - expect(comp.error).toBe('ERROR'); - expect(comp.success).toEqual(null); - }) - )); - }); -}); diff --git a/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts b/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts deleted file mode 100644 index a4c88f46..00000000 --- a/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { ComponentFixture, TestBed, inject, tick, fakeAsync } from '@angular/core/testing'; -import { Observable, of, throwError } from 'rxjs'; -import { Renderer, ElementRef } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { HsadminNgTestModule } from '../../../../test.module'; -import { PasswordResetFinishComponent } from 'app/account/password-reset/finish/password-reset-finish.component'; -import { PasswordResetFinishService } from 'app/account/password-reset/finish/password-reset-finish.service'; -import { MockActivatedRoute } from '../../../../helpers/mock-route.service'; - -describe('Component Tests', () => { - describe('PasswordResetFinishComponent', () => { - let fixture: ComponentFixture; - let comp: PasswordResetFinishComponent; - - beforeEach(() => { - fixture = TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [PasswordResetFinishComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: new MockActivatedRoute({ key: 'XYZPDQ' }) - }, - { - provide: Renderer, - useValue: { - invokeElementMethod(renderElement: any, methodName: string, args?: any[]) {} - } - }, - { - provide: ElementRef, - useValue: new ElementRef(null) - } - ] - }) - .overrideTemplate(PasswordResetFinishComponent, '') - .createComponent(PasswordResetFinishComponent); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(PasswordResetFinishComponent); - comp = fixture.componentInstance; - comp.ngOnInit(); - }); - - it('should define its initial state', () => { - comp.ngOnInit(); - - expect(comp.keyMissing).toBeFalsy(); - expect(comp.key).toEqual('XYZPDQ'); - expect(comp.resetAccount).toEqual({}); - }); - - it('sets focus after the view has been initialized', inject([ElementRef], (elementRef: ElementRef) => { - const element = fixture.nativeElement; - const node = { - focus() {} - }; - - elementRef.nativeElement = element; - spyOn(element, 'querySelector').and.returnValue(node); - spyOn(node, 'focus'); - - comp.ngAfterViewInit(); - - expect(element.querySelector).toHaveBeenCalledWith('#password'); - expect(node.focus).toHaveBeenCalled(); - })); - - it('should ensure the two passwords entered match', () => { - comp.resetAccount.password = 'password'; - comp.confirmPassword = 'non-matching'; - - comp.finishReset(); - - expect(comp.doNotMatch).toEqual('ERROR'); - }); - - it('should update success to OK after resetting password', inject( - [PasswordResetFinishService], - fakeAsync((service: PasswordResetFinishService) => { - spyOn(service, 'save').and.returnValue(of({})); - - comp.resetAccount.password = 'password'; - comp.confirmPassword = 'password'; - - comp.finishReset(); - tick(); - - expect(service.save).toHaveBeenCalledWith({ - key: 'XYZPDQ', - newPassword: 'password' - }); - expect(comp.success).toEqual('OK'); - }) - )); - - it('should notify of generic error', inject( - [PasswordResetFinishService], - fakeAsync((service: PasswordResetFinishService) => { - spyOn(service, 'save').and.returnValue(throwError('ERROR')); - - comp.resetAccount.password = 'password'; - comp.confirmPassword = 'password'; - - comp.finishReset(); - tick(); - - expect(service.save).toHaveBeenCalledWith({ - key: 'XYZPDQ', - newPassword: 'password' - }); - expect(comp.success).toBeNull(); - expect(comp.error).toEqual('ERROR'); - }) - )); - }); -}); diff --git a/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts b/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts deleted file mode 100644 index 3d188868..00000000 --- a/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; -import { Renderer, ElementRef } from '@angular/core'; -import { Observable, of, throwError } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../../test.module'; -import { PasswordResetInitComponent } from 'app/account/password-reset/init/password-reset-init.component'; -import { PasswordResetInitService } from 'app/account/password-reset/init/password-reset-init.service'; -import { EMAIL_NOT_FOUND_TYPE } from 'app/shared'; - -describe('Component Tests', () => { - describe('PasswordResetInitComponent', () => { - let fixture: ComponentFixture; - let comp: PasswordResetInitComponent; - - beforeEach(() => { - fixture = TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [PasswordResetInitComponent], - providers: [ - { - provide: Renderer, - useValue: { - invokeElementMethod(renderElement: any, methodName: string, args?: any[]) {} - } - }, - { - provide: ElementRef, - useValue: new ElementRef(null) - } - ] - }) - .overrideTemplate(PasswordResetInitComponent, '') - .createComponent(PasswordResetInitComponent); - comp = fixture.componentInstance; - comp.ngOnInit(); - }); - - it('should define its initial state', () => { - expect(comp.success).toBeUndefined(); - expect(comp.error).toBeUndefined(); - expect(comp.errorEmailNotExists).toBeUndefined(); - expect(comp.resetAccount).toEqual({}); - }); - - it('sets focus after the view has been initialized', inject([ElementRef], (elementRef: ElementRef) => { - const element = fixture.nativeElement; - const node = { - focus() {} - }; - - elementRef.nativeElement = element; - spyOn(element, 'querySelector').and.returnValue(node); - spyOn(node, 'focus'); - - comp.ngAfterViewInit(); - - expect(element.querySelector).toHaveBeenCalledWith('#email'); - expect(node.focus).toHaveBeenCalled(); - })); - - it('notifies of success upon successful requestReset', inject([PasswordResetInitService], (service: PasswordResetInitService) => { - spyOn(service, 'save').and.returnValue(of({})); - comp.resetAccount.email = 'user@domain.com'; - - comp.requestReset(); - - expect(service.save).toHaveBeenCalledWith('user@domain.com'); - expect(comp.success).toEqual('OK'); - expect(comp.error).toBeNull(); - expect(comp.errorEmailNotExists).toBeNull(); - })); - - it('notifies of unknown email upon email address not registered/400', inject( - [PasswordResetInitService], - (service: PasswordResetInitService) => { - spyOn(service, 'save').and.returnValue( - throwError({ - status: 400, - error: { type: EMAIL_NOT_FOUND_TYPE } - }) - ); - comp.resetAccount.email = 'user@domain.com'; - - comp.requestReset(); - - expect(service.save).toHaveBeenCalledWith('user@domain.com'); - expect(comp.success).toBeNull(); - expect(comp.error).toBeNull(); - expect(comp.errorEmailNotExists).toEqual('ERROR'); - } - )); - - it('notifies of error upon error response', inject([PasswordResetInitService], (service: PasswordResetInitService) => { - spyOn(service, 'save').and.returnValue( - throwError({ - status: 503, - data: 'something else' - }) - ); - comp.resetAccount.email = 'user@domain.com'; - - comp.requestReset(); - - expect(service.save).toHaveBeenCalledWith('user@domain.com'); - expect(comp.success).toBeNull(); - expect(comp.errorEmailNotExists).toBeNull(); - expect(comp.error).toEqual('ERROR'); - })); - }); -}); diff --git a/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts b/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts deleted file mode 100644 index 35e923ac..00000000 --- a/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; - -import { PasswordStrengthBarComponent } from 'app/account/password/password-strength-bar.component'; - -describe('Component Tests', () => { - describe('PasswordStrengthBarComponent', () => { - let comp: PasswordStrengthBarComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [PasswordStrengthBarComponent] - }) - .overrideTemplate(PasswordStrengthBarComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(PasswordStrengthBarComponent); - comp = fixture.componentInstance; - }); - - describe('PasswordStrengthBarComponents', () => { - it('should initialize with default values', () => { - expect(comp.measureStrength('')).toBe(0); - expect(comp.colors).toEqual(['#F00', '#F90', '#FF0', '#9F0', '#0F0']); - expect(comp.getColor(0).idx).toBe(1); - expect(comp.getColor(0).col).toBe(comp.colors[0]); - }); - - it('should increase strength upon password value change', () => { - expect(comp.measureStrength('')).toBe(0); - expect(comp.measureStrength('aa')).toBeGreaterThanOrEqual(comp.measureStrength('')); - expect(comp.measureStrength('aa^6')).toBeGreaterThanOrEqual(comp.measureStrength('aa')); - expect(comp.measureStrength('Aa090(**)')).toBeGreaterThanOrEqual(comp.measureStrength('aa^6')); - expect(comp.measureStrength('Aa090(**)+-07365')).toBeGreaterThanOrEqual(comp.measureStrength('Aa090(**)')); - }); - - it('should change the color based on strength', () => { - expect(comp.getColor(0).col).toBe(comp.colors[0]); - expect(comp.getColor(11).col).toBe(comp.colors[1]); - expect(comp.getColor(22).col).toBe(comp.colors[2]); - expect(comp.getColor(33).col).toBe(comp.colors[3]); - expect(comp.getColor(44).col).toBe(comp.colors[4]); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/account/password/password.component.spec.ts b/src/test/javascript/spec/app/account/password/password.component.spec.ts deleted file mode 100644 index ab3656fc..00000000 --- a/src/test/javascript/spec/app/account/password/password.component.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; -import { HttpResponse } from '@angular/common/http'; -import { Observable, of, throwError } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { PasswordComponent } from 'app/account/password/password.component'; -import { PasswordService } from 'app/account/password/password.service'; - -describe('Component Tests', () => { - describe('PasswordComponent', () => { - let comp: PasswordComponent; - let fixture: ComponentFixture; - let service: PasswordService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [PasswordComponent], - providers: [] - }) - .overrideTemplate(PasswordComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(PasswordComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(PasswordService); - }); - - it('should show error if passwords do not match', () => { - // GIVEN - comp.newPassword = 'password1'; - comp.confirmPassword = 'password2'; - // WHEN - comp.changePassword(); - // THEN - expect(comp.doNotMatch).toBe('ERROR'); - expect(comp.error).toBeNull(); - expect(comp.success).toBeNull(); - }); - - it('should call Auth.changePassword when passwords match', () => { - // GIVEN - const passwordValues = { - currentPassword: 'oldPassword', - newPassword: 'myPassword' - }; - - spyOn(service, 'save').and.returnValue(of(new HttpResponse({ body: true }))); - comp.currentPassword = passwordValues.currentPassword; - comp.newPassword = comp.confirmPassword = passwordValues.newPassword; - - // WHEN - comp.changePassword(); - - // THEN - expect(service.save).toHaveBeenCalledWith(passwordValues.newPassword, passwordValues.currentPassword); - }); - - it('should set success to OK upon success', function() { - // GIVEN - spyOn(service, 'save').and.returnValue(of(new HttpResponse({ body: true }))); - comp.newPassword = comp.confirmPassword = 'myPassword'; - - // WHEN - comp.changePassword(); - - // THEN - expect(comp.doNotMatch).toBeNull(); - expect(comp.error).toBeNull(); - expect(comp.success).toBe('OK'); - }); - - it('should notify of error if change password fails', function() { - // GIVEN - spyOn(service, 'save').and.returnValue(throwError('ERROR')); - comp.newPassword = comp.confirmPassword = 'myPassword'; - - // WHEN - comp.changePassword(); - - // THEN - expect(comp.doNotMatch).toBeNull(); - expect(comp.success).toBeNull(); - expect(comp.error).toBe('ERROR'); - }); - }); -}); diff --git a/src/test/javascript/spec/app/account/register/register.component.spec.ts b/src/test/javascript/spec/app/account/register/register.component.spec.ts deleted file mode 100644 index bec63e5a..00000000 --- a/src/test/javascript/spec/app/account/register/register.component.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { ComponentFixture, TestBed, async, inject, tick, fakeAsync } from '@angular/core/testing'; -import { Observable, of, throwError } from 'rxjs'; - -import { JhiLanguageService } from 'ng-jhipster'; -import { MockLanguageService } from '../../../helpers/mock-language.service'; -import { HsadminNgTestModule } from '../../../test.module'; -import { EMAIL_ALREADY_USED_TYPE, LOGIN_ALREADY_USED_TYPE } from 'app/shared'; -import { Register } from 'app/account/register/register.service'; -import { RegisterComponent } from 'app/account/register/register.component'; - -describe('Component Tests', () => { - describe('RegisterComponent', () => { - let fixture: ComponentFixture; - let comp: RegisterComponent; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [RegisterComponent] - }) - .overrideTemplate(RegisterComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(RegisterComponent); - comp = fixture.componentInstance; - comp.ngOnInit(); - }); - - it('should ensure the two passwords entered match', () => { - comp.registerAccount.password = 'password'; - comp.confirmPassword = 'non-matching'; - - comp.register(); - - expect(comp.doNotMatch).toEqual('ERROR'); - }); - - it('should update success to OK after creating an account', inject( - [Register, JhiLanguageService], - fakeAsync((service: Register, mockTranslate: MockLanguageService) => { - spyOn(service, 'save').and.returnValue(of({})); - comp.registerAccount.password = comp.confirmPassword = 'password'; - - comp.register(); - tick(); - - expect(service.save).toHaveBeenCalledWith({ - password: 'password', - langKey: 'de' - }); - expect(comp.success).toEqual(true); - expect(comp.registerAccount.langKey).toEqual('de'); - expect(mockTranslate.getCurrentSpy).toHaveBeenCalled(); - expect(comp.errorUserExists).toBeNull(); - expect(comp.errorEmailExists).toBeNull(); - expect(comp.error).toBeNull(); - }) - )); - - it('should notify of user existence upon 400/login already in use', inject( - [Register], - fakeAsync((service: Register) => { - spyOn(service, 'save').and.returnValue( - throwError({ - status: 400, - error: { type: LOGIN_ALREADY_USED_TYPE } - }) - ); - comp.registerAccount.password = comp.confirmPassword = 'password'; - - comp.register(); - tick(); - - expect(comp.errorUserExists).toEqual('ERROR'); - expect(comp.errorEmailExists).toBeNull(); - expect(comp.error).toBeNull(); - }) - )); - - it('should notify of email existence upon 400/email address already in use', inject( - [Register], - fakeAsync((service: Register) => { - spyOn(service, 'save').and.returnValue( - throwError({ - status: 400, - error: { type: EMAIL_ALREADY_USED_TYPE } - }) - ); - comp.registerAccount.password = comp.confirmPassword = 'password'; - - comp.register(); - tick(); - - expect(comp.errorEmailExists).toEqual('ERROR'); - expect(comp.errorUserExists).toBeNull(); - expect(comp.error).toBeNull(); - }) - )); - - it('should notify of generic error', inject( - [Register], - fakeAsync((service: Register) => { - spyOn(service, 'save').and.returnValue( - throwError({ - status: 503 - }) - ); - comp.registerAccount.password = comp.confirmPassword = 'password'; - - comp.register(); - tick(); - - expect(comp.errorUserExists).toBeNull(); - expect(comp.errorEmailExists).toBeNull(); - expect(comp.error).toEqual('ERROR'); - }) - )); - }); -}); diff --git a/src/test/javascript/spec/app/account/settings/settings.component.spec.ts b/src/test/javascript/spec/app/account/settings/settings.component.spec.ts deleted file mode 100644 index af1a4fda..00000000 --- a/src/test/javascript/spec/app/account/settings/settings.component.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; -import { Observable, throwError } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { AccountService } from 'app/core'; -import { SettingsComponent } from 'app/account/settings/settings.component'; - -describe('Component Tests', () => { - describe('SettingsComponent', () => { - let comp: SettingsComponent; - let fixture: ComponentFixture; - let mockAuth: any; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [SettingsComponent], - providers: [] - }) - .overrideTemplate(SettingsComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SettingsComponent); - comp = fixture.componentInstance; - mockAuth = fixture.debugElement.injector.get(AccountService); - }); - - it('should send the current identity upon save', () => { - // GIVEN - const accountValues = { - firstName: 'John', - lastName: 'Doe', - - activated: true, - email: 'john.doe@mail.com', - langKey: 'de', - login: 'john' - }; - mockAuth.setIdentityResponse(accountValues); - - // WHEN - comp.settingsAccount = accountValues; - comp.save(); - - // THEN - expect(mockAuth.identitySpy).toHaveBeenCalled(); - expect(mockAuth.saveSpy).toHaveBeenCalledWith(accountValues); - expect(comp.settingsAccount).toEqual(accountValues); - }); - - it('should notify of success upon successful save', () => { - // GIVEN - const accountValues = { - firstName: 'John', - lastName: 'Doe' - }; - mockAuth.setIdentityResponse(accountValues); - - // WHEN - comp.save(); - - // THEN - expect(comp.error).toBeNull(); - expect(comp.success).toBe('OK'); - }); - - it('should notify of error upon failed save', () => { - // GIVEN - mockAuth.saveSpy.and.returnValue(throwError('ERROR')); - - // WHEN - comp.save(); - - // THEN - expect(comp.error).toEqual('ERROR'); - expect(comp.success).toBeNull(); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts b/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts deleted file mode 100644 index da1b268c..00000000 --- a/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { AuditsComponent } from 'app/admin/audits/audits.component'; -import { AuditsService } from 'app/admin/audits/audits.service'; -import { Audit } from 'app/admin/audits/audit.model'; -import { ITEMS_PER_PAGE } from 'app/shared'; - -function build2DigitsDatePart(datePart: number) { - return `0${datePart}`.slice(-2); -} - -function getDate(isToday = true) { - let date: Date = new Date(); - if (isToday) { - // Today + 1 day - needed if the current day must be included - date.setDate(date.getDate() + 1); - } else { - // get last month - if (date.getMonth() === 0) { - date = new Date(date.getFullYear() - 1, 11, date.getDate()); - } else { - date = new Date(date.getFullYear(), date.getMonth() - 1, date.getDate()); - } - } - const monthString = build2DigitsDatePart(date.getMonth() + 1); - const dateString = build2DigitsDatePart(date.getDate()); - return `${date.getFullYear()}-${monthString}-${dateString}`; -} - -describe('Component Tests', () => { - describe('AuditsComponent', () => { - let comp: AuditsComponent; - let fixture: ComponentFixture; - let service: AuditsService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [AuditsComponent], - providers: [AuditsService] - }) - .overrideTemplate(AuditsComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(AuditsComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(AuditsService); - }); - - describe('today function ', () => { - it('should set toDate to current date', () => { - comp.today(); - expect(comp.toDate).toBe(getDate()); - }); - }); - - describe('previousMonth function ', () => { - it('should set fromDate to current date', () => { - comp.previousMonth(); - expect(comp.fromDate).toBe(getDate(false)); - }); - }); - - describe('By default, on init', () => { - it('should set all default values correctly', () => { - fixture.detectChanges(); - expect(comp.toDate).toBe(getDate()); - expect(comp.fromDate).toBe(getDate(false)); - expect(comp.itemsPerPage).toBe(ITEMS_PER_PAGE); - expect(comp.page).toBe(10); - expect(comp.reverse).toBeFalsy(); - expect(comp.predicate).toBe('id'); - }); - }); - - describe('OnInit', () => { - it('Should call load all on init', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - const audit = new Audit({ remoteAddress: '127.0.0.1', sessionId: '123' }, 'user', '20140101', 'AUTHENTICATION_SUCCESS'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [audit], - headers - }) - ) - ); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.audits[0]).toEqual(jasmine.objectContaining(audit)); - }); - }); - - describe('Create sort object', () => { - it('Should sort only by id asc', () => { - // GIVEN - comp.predicate = 'id'; - comp.reverse = false; - - // WHEN - const sort = comp.sort(); - - // THEN - expect(sort.length).toEqual(1); - expect(sort[0]).toEqual('id,desc'); - }); - - it('Should sort by timestamp asc then by id', () => { - // GIVEN - comp.predicate = 'timestamp'; - comp.reverse = true; - - // WHEN - const sort = comp.sort(); - - // THEN - expect(sort.length).toEqual(2); - expect(sort[0]).toEqual('timestamp,asc'); - expect(sort[1]).toEqual('id'); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/audits/audits.service.spec.ts b/src/test/javascript/spec/app/admin/audits/audits.service.spec.ts deleted file mode 100644 index 84ff79f6..00000000 --- a/src/test/javascript/spec/app/admin/audits/audits.service.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { AuditsService } from 'app/admin/audits/audits.service'; -import { Audit } from 'app/admin/audits/audit.model'; -import { SERVER_API_URL } from 'app/app.constants'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; - -describe('Service Tests', () => { - describe('Audits Service', () => { - let service: AuditsService; - let httpMock; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - - service = TestBed.get(AuditsService); - httpMock = TestBed.get(HttpTestingController); - }); - - afterEach(() => { - httpMock.verify(); - }); - - describe('Service methods', () => { - it('should call correct URL', () => { - service.query({}).subscribe(() => {}); - - const req = httpMock.expectOne({ method: 'GET' }); - const resourceUrl = SERVER_API_URL + 'management/audits'; - expect(req.request.url).toEqual(resourceUrl); - }); - - it('should return Audits', () => { - const audit = new Audit({ remoteAddress: '127.0.0.1', sessionId: '123' }, 'user', '20140101', 'AUTHENTICATION_SUCCESS'); - - service.query({}).subscribe(received => { - expect(received.body[0]).toEqual(audit); - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush([audit]); - }); - - it('should propagate not found response', () => { - service.query({}).subscribe(null, (_error: any) => { - expect(_error.status).toEqual(404); - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush('Invalid request parameters', { - status: 404, - statusText: 'Bad Request' - }); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/configuration/configuration.component.spec.ts b/src/test/javascript/spec/app/admin/configuration/configuration.component.spec.ts deleted file mode 100644 index 175657e5..00000000 --- a/src/test/javascript/spec/app/admin/configuration/configuration.component.spec.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; -import { of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { JhiConfigurationComponent } from 'app/admin/configuration/configuration.component'; -import { JhiConfigurationService } from 'app/admin/configuration/configuration.service'; -import { ITEMS_PER_PAGE } from 'app/shared'; -import { Log } from 'app/admin'; - -describe('Component Tests', () => { - describe('JhiConfigurationComponent', () => { - let comp: JhiConfigurationComponent; - let fixture: ComponentFixture; - let service: JhiConfigurationService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [JhiConfigurationComponent], - providers: [JhiConfigurationService] - }) - .overrideTemplate(JhiConfigurationComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(JhiConfigurationComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(JhiConfigurationService); - }); - - describe('OnInit', () => { - it('should set all default values correctly', () => { - expect(comp.configKeys).toEqual([]); - expect(comp.filter).toBe(''); - expect(comp.orderProp).toBe('prefix'); - expect(comp.reverse).toBe(false); - }); - it('Should call load all on init', () => { - // GIVEN - const body = [{ config: 'test', properties: 'test' }, { config: 'test2' }]; - const envConfig = { envConfig: 'test' }; - spyOn(service, 'get').and.returnValue(of(body)); - spyOn(service, 'getEnv').and.returnValue(of(envConfig)); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.get).toHaveBeenCalled(); - expect(service.getEnv).toHaveBeenCalled(); - expect(comp.configKeys).toEqual([['0', '1', '2', '3']]); - expect(comp.allConfiguration).toEqual(envConfig); - }); - }); - describe('keys method', () => { - it('should return the keys of an Object', () => { - // GIVEN - const data = { - key1: 'test', - key2: 'test2' - }; - - // THEN - expect(comp.keys(data)).toEqual(['key1', 'key2']); - expect(comp.keys(undefined)).toEqual([]); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/configuration/configuration.service.spec.ts b/src/test/javascript/spec/app/admin/configuration/configuration.service.spec.ts deleted file mode 100644 index 6039044b..00000000 --- a/src/test/javascript/spec/app/admin/configuration/configuration.service.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { JhiConfigurationService } from 'app/admin/configuration/configuration.service'; -import { SERVER_API_URL } from 'app/app.constants'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { HttpResponse } from '@angular/common/http'; - -describe('Service Tests', () => { - describe('Logs Service', () => { - let service: JhiConfigurationService; - let httpMock; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - - service = TestBed.get(JhiConfigurationService); - httpMock = TestBed.get(HttpTestingController); - }); - - afterEach(() => { - httpMock.verify(); - }); - - describe('Service methods', () => { - it('should call correct URL', () => { - service.get().subscribe(() => {}); - - const req = httpMock.expectOne({ method: 'GET' }); - const resourceUrl = SERVER_API_URL + 'management/configprops'; - expect(req.request.url).toEqual(resourceUrl); - }); - - it('should get the config', () => { - const angularConfig = { - contexts: { - angular: { - beans: ['test2'] - } - } - }; - service.get().subscribe(received => { - expect(received.body[0]).toEqual(angularConfig); - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(angularConfig); - }); - - it('should get the env', () => { - const propertySources = new HttpResponse({ - body: [{ name: 'test1', properties: 'test1' }, { name: 'test2', properties: 'test2' }] - }); - service.get().subscribe(received => { - expect(received.body[0]).toEqual(propertySources); - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(propertySources); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/health/health.component.spec.ts b/src/test/javascript/spec/app/admin/health/health.component.spec.ts deleted file mode 100644 index 731a6089..00000000 --- a/src/test/javascript/spec/app/admin/health/health.component.spec.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; -import { HttpResponse, HttpErrorResponse } from '@angular/common/http'; -import { of, throwError } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { JhiHealthCheckComponent } from 'app/admin/health/health.component'; -import { JhiHealthService } from 'app/admin/health/health.service'; - -describe('Component Tests', () => { - describe('JhiHealthCheckComponent', () => { - let comp: JhiHealthCheckComponent; - let fixture: ComponentFixture; - let service: JhiHealthService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [JhiHealthCheckComponent] - }) - .overrideTemplate(JhiHealthCheckComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(JhiHealthCheckComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(JhiHealthService); - }); - - describe('baseName and subSystemName', () => { - it('should return the basename when it has no sub system', () => { - expect(comp.baseName('base')).toBe('base'); - }); - - it('should return the basename when it has sub systems', () => { - expect(comp.baseName('base.subsystem.system')).toBe('base'); - }); - - it('should return the sub system name', () => { - expect(comp.subSystemName('subsystem')).toBe(''); - }); - - it('should return the subsystem when it has multiple keys', () => { - expect(comp.subSystemName('subsystem.subsystem.system')).toBe(' - subsystem.system'); - }); - }); - - describe('transformHealthData', () => { - it('should flatten empty health data', () => { - const data = {}; - const expected = []; - expect(service.transformHealthData(data)).toEqual(expected); - }); - - it('should flatten health data with no subsystems', () => { - const data = { - details: { - status: 'UP', - db: { - status: 'UP', - database: 'H2', - hello: '1' - }, - mail: { - status: 'UP', - error: 'mail.a.b.c' - } - } - }; - const expected = [ - { - name: 'db', - status: 'UP', - details: { - database: 'H2', - hello: '1' - } - }, - { - name: 'mail', - error: 'mail.a.b.c', - status: 'UP' - } - ]; - expect(service.transformHealthData(data)).toEqual(expected); - }); - - it('should flatten health data with subsystems at level 1, main system has no additional information', () => { - const data = { - details: { - status: 'UP', - db: { - status: 'UP', - database: 'H2', - hello: '1' - }, - mail: { - status: 'UP', - error: 'mail.a.b.c' - }, - system: { - status: 'DOWN', - subsystem1: { - status: 'UP', - property1: 'system.subsystem1.property1' - }, - subsystem2: { - status: 'DOWN', - error: 'system.subsystem1.error', - property2: 'system.subsystem2.property2' - } - } - } - }; - const expected = [ - { - name: 'db', - status: 'UP', - details: { - database: 'H2', - hello: '1' - } - }, - { - name: 'mail', - error: 'mail.a.b.c', - status: 'UP' - }, - { - name: 'system.subsystem1', - status: 'UP', - details: { - property1: 'system.subsystem1.property1' - } - }, - { - name: 'system.subsystem2', - error: 'system.subsystem1.error', - status: 'DOWN', - details: { - property2: 'system.subsystem2.property2' - } - } - ]; - expect(service.transformHealthData(data)).toEqual(expected); - }); - - it('should flatten health data with subsystems at level 1, main system has additional information', () => { - const data = { - details: { - status: 'UP', - db: { - status: 'UP', - database: 'H2', - hello: '1' - }, - mail: { - status: 'UP', - error: 'mail.a.b.c' - }, - system: { - status: 'DOWN', - property1: 'system.property1', - subsystem1: { - status: 'UP', - property1: 'system.subsystem1.property1' - }, - subsystem2: { - status: 'DOWN', - error: 'system.subsystem1.error', - property2: 'system.subsystem2.property2' - } - } - } - }; - const expected = [ - { - name: 'db', - status: 'UP', - details: { - database: 'H2', - hello: '1' - } - }, - { - name: 'mail', - error: 'mail.a.b.c', - status: 'UP' - }, - { - name: 'system', - status: 'DOWN', - details: { - property1: 'system.property1' - } - }, - { - name: 'system.subsystem1', - status: 'UP', - details: { - property1: 'system.subsystem1.property1' - } - }, - { - name: 'system.subsystem2', - error: 'system.subsystem1.error', - status: 'DOWN', - details: { - property2: 'system.subsystem2.property2' - } - } - ]; - expect(service.transformHealthData(data)).toEqual(expected); - }); - - it('should flatten health data with subsystems at level 1, main system has additional error', () => { - const data = { - details: { - status: 'UP', - db: { - status: 'UP', - database: 'H2', - hello: '1' - }, - mail: { - status: 'UP', - error: 'mail.a.b.c' - }, - system: { - status: 'DOWN', - error: 'show me', - subsystem1: { - status: 'UP', - property1: 'system.subsystem1.property1' - }, - subsystem2: { - status: 'DOWN', - error: 'system.subsystem1.error', - property2: 'system.subsystem2.property2' - } - } - } - }; - const expected = [ - { - name: 'db', - status: 'UP', - details: { - database: 'H2', - hello: '1' - } - }, - { - name: 'mail', - error: 'mail.a.b.c', - status: 'UP' - }, - { - name: 'system', - error: 'show me', - status: 'DOWN' - }, - { - name: 'system.subsystem1', - status: 'UP', - details: { - property1: 'system.subsystem1.property1' - } - }, - { - name: 'system.subsystem2', - error: 'system.subsystem1.error', - status: 'DOWN', - details: { - property2: 'system.subsystem2.property2' - } - } - ]; - expect(service.transformHealthData(data)).toEqual(expected); - }); - }); - - describe('getBadgeClass', () => { - it('should get badge class', () => { - const upBadgeClass = comp.getBadgeClass('UP'); - const downBadgeClass = comp.getBadgeClass('DOWN'); - expect(upBadgeClass).toEqual('badge-success'); - expect(downBadgeClass).toEqual('badge-danger'); - }); - }); - - describe('refresh', () => { - it('should call refresh on init', () => { - // GIVEN - spyOn(service, 'checkHealth').and.returnValue(of(new HttpResponse())); - spyOn(service, 'transformHealthData').and.returnValue(of({ data: 'test' })); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.checkHealth).toHaveBeenCalled(); - expect(service.transformHealthData).toHaveBeenCalled(); - expect(comp.healthData.value).toEqual({ data: 'test' }); - }); - it('should handle a 503 on refreshing health data', () => { - // GIVEN - spyOn(service, 'checkHealth').and.returnValue(throwError(new HttpErrorResponse({ status: 503, error: 'Mail down' }))); - spyOn(service, 'transformHealthData').and.returnValue(of({ health: 'down' })); - - // WHEN - comp.refresh(); - - // THEN - expect(service.checkHealth).toHaveBeenCalled(); - expect(service.transformHealthData).toHaveBeenCalled(); - expect(comp.healthData.value).toEqual({ health: 'down' }); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/logs/logs.component.spec.ts b/src/test/javascript/spec/app/admin/logs/logs.component.spec.ts deleted file mode 100644 index da5a88a8..00000000 --- a/src/test/javascript/spec/app/admin/logs/logs.component.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; -import { of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { LogsComponent } from 'app/admin/logs/logs.component'; -import { LogsService } from 'app/admin/logs/logs.service'; -import { ITEMS_PER_PAGE } from 'app/shared'; -import { Log } from 'app/admin'; - -describe('Component Tests', () => { - describe('LogsComponent', () => { - let comp: LogsComponent; - let fixture: ComponentFixture; - let service: LogsService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [LogsComponent], - providers: [LogsService] - }) - .overrideTemplate(LogsComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(LogsComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(LogsService); - }); - - describe('OnInit', () => { - it('should set all default values correctly', () => { - expect(comp.filter).toBe(''); - expect(comp.orderProp).toBe('name'); - expect(comp.reverse).toBe(false); - }); - it('Should call load all on init', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - const log = new Log('main', 'WARN'); - spyOn(service, 'findAll').and.returnValue( - of( - new HttpResponse({ - body: [log], - headers - }) - ) - ); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.findAll).toHaveBeenCalled(); - expect(comp.loggers[0]).toEqual(jasmine.objectContaining(log)); - }); - }); - describe('change log level', () => { - it('should change log level correctly', () => { - // GIVEN - const log = new Log('main', 'ERROR'); - spyOn(service, 'changeLevel').and.returnValue(of(new HttpResponse())); - spyOn(service, 'findAll').and.returnValue(of(new HttpResponse({ body: [log] }))); - - // WHEN - comp.changeLevel('main', 'ERROR'); - - // THEN - expect(service.changeLevel).toHaveBeenCalled(); - expect(service.findAll).toHaveBeenCalled(); - expect(comp.loggers[0]).toEqual(jasmine.objectContaining(log)); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/logs/logs.service.spec.ts b/src/test/javascript/spec/app/admin/logs/logs.service.spec.ts deleted file mode 100644 index c3483392..00000000 --- a/src/test/javascript/spec/app/admin/logs/logs.service.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { LogsService } from 'app/admin/logs/logs.service'; -import { Log } from 'app/admin/logs/log.model'; -import { SERVER_API_URL } from 'app/app.constants'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; - -describe('Service Tests', () => { - describe('Logs Service', () => { - let service: LogsService; - let httpMock; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - - service = TestBed.get(LogsService); - httpMock = TestBed.get(HttpTestingController); - }); - - afterEach(() => { - httpMock.verify(); - }); - - describe('Service methods', () => { - it('should call correct URL', () => { - service.findAll().subscribe(() => {}); - - const req = httpMock.expectOne({ method: 'GET' }); - const resourceUrl = SERVER_API_URL + 'management/logs'; - expect(req.request.url).toEqual(resourceUrl); - }); - - it('should return Logs', () => { - const log = new Log('main', 'ERROR'); - - service.findAll().subscribe(received => { - expect(received.body[0]).toEqual(log); - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush([log]); - }); - - it('should change log level', () => { - const log = new Log('main', 'ERROR'); - - service.changeLevel(log).subscribe(received => { - expect(received.body[0]).toEqual(log); - }); - - const req = httpMock.expectOne({ method: 'PUT' }); - req.flush([log]); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/metrics/metrics.component.spec.ts b/src/test/javascript/spec/app/admin/metrics/metrics.component.spec.ts deleted file mode 100644 index 94acaac1..00000000 --- a/src/test/javascript/spec/app/admin/metrics/metrics.component.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; -import { HttpResponse, HttpErrorResponse } from '@angular/common/http'; -import { of, throwError } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { JhiMetricsMonitoringComponent } from 'app/admin/metrics/metrics.component'; -import { JhiMetricsService } from 'app/admin/metrics/metrics.service'; - -describe('Component Tests', () => { - describe('JhiMetricsMonitoringComponent', () => { - let comp: JhiMetricsMonitoringComponent; - let fixture: ComponentFixture; - let service: JhiMetricsService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [JhiMetricsMonitoringComponent] - }) - .overrideTemplate(JhiMetricsMonitoringComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(JhiMetricsMonitoringComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(JhiMetricsService); - }); - - describe('refresh', () => { - it('should call refresh on init', () => { - // GIVEN - const response = { - timers: { - service: 'test', - unrelatedKey: 'test' - }, - gauges: { - 'jcache.statistics': { - value: 2 - }, - unrelatedKey: 'test' - } - }; - spyOn(service, 'getMetrics').and.returnValue(of(response)); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.getMetrics).toHaveBeenCalled(); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/metrics/metrics.service.spec.ts b/src/test/javascript/spec/app/admin/metrics/metrics.service.spec.ts deleted file mode 100644 index 2c3665b0..00000000 --- a/src/test/javascript/spec/app/admin/metrics/metrics.service.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { JhiMetricsService } from 'app/admin/metrics/metrics.service'; -import { SERVER_API_URL } from 'app/app.constants'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; - -describe('Service Tests', () => { - describe('Logs Service', () => { - let service: JhiMetricsService; - let httpMock; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - - service = TestBed.get(JhiMetricsService); - httpMock = TestBed.get(HttpTestingController); - }); - - afterEach(() => { - httpMock.verify(); - }); - - describe('Service methods', () => { - it('should call correct URL', () => { - service.getMetrics().subscribe(() => {}); - - const req = httpMock.expectOne({ method: 'GET' }); - const resourceUrl = SERVER_API_URL + 'management/jhi-metrics'; - expect(req.request.url).toEqual(resourceUrl); - }); - - it('should return Metrics', () => { - const metrics = []; - - service.getMetrics().subscribe(received => { - expect(received.body[0]).toEqual(metrics); - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush([metrics]); - }); - - it('should return Thread Dump', () => { - const dump = [{ name: 'test1', threadState: 'RUNNABLE' }]; - - service.threadDump().subscribe(received => { - expect(received.body[0]).toEqual(dump); - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush([dump]); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/user-management/user-management-delete-dialog.component.spec.ts b/src/test/javascript/spec/app/admin/user-management/user-management-delete-dialog.component.spec.ts deleted file mode 100644 index 4108ebd2..00000000 --- a/src/test/javascript/spec/app/admin/user-management/user-management-delete-dialog.component.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { ComponentFixture, TestBed, async, inject, fakeAsync, tick } from '@angular/core/testing'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Observable, of } from 'rxjs'; -import { JhiEventManager } from 'ng-jhipster'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { UserMgmtDeleteDialogComponent } from 'app/admin/user-management/user-management-delete-dialog.component'; -import { UserService } from 'app/core'; - -describe('Component Tests', () => { - describe('User Management Delete Component', () => { - let comp: UserMgmtDeleteDialogComponent; - let fixture: ComponentFixture; - let service: UserService; - let mockEventManager: any; - let mockActiveModal: any; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [UserMgmtDeleteDialogComponent] - }) - .overrideTemplate(UserMgmtDeleteDialogComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(UserMgmtDeleteDialogComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(UserService); - mockEventManager = fixture.debugElement.injector.get(JhiEventManager); - mockActiveModal = fixture.debugElement.injector.get(NgbActiveModal); - }); - - describe('confirmDelete', () => { - it('Should call delete service on confirmDelete', inject( - [], - fakeAsync(() => { - // GIVEN - spyOn(service, 'delete').and.returnValue(of({})); - - // WHEN - comp.confirmDelete('user'); - tick(); - - // THEN - expect(service.delete).toHaveBeenCalledWith('user'); - expect(mockActiveModal.dismissSpy).toHaveBeenCalled(); - expect(mockEventManager.broadcastSpy).toHaveBeenCalled(); - }) - )); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/user-management/user-management-detail.component.spec.ts b/src/test/javascript/spec/app/admin/user-management/user-management-detail.component.spec.ts deleted file mode 100644 index cd3bc9bf..00000000 --- a/src/test/javascript/spec/app/admin/user-management/user-management-detail.component.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { UserMgmtDetailComponent } from 'app/admin/user-management/user-management-detail.component'; -import { User } from 'app/core'; - -describe('Component Tests', () => { - describe('User Management Detail Component', () => { - let comp: UserMgmtDetailComponent; - let fixture: ComponentFixture; - const route = ({ - data: of({ user: new User(1, 'user', 'first', 'last', 'first@last.com', true, 'en', ['ROLE_USER'], 'admin', null, null, null) }) - } as any) as ActivatedRoute; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [UserMgmtDetailComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: route - } - ] - }) - .overrideTemplate(UserMgmtDetailComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(UserMgmtDetailComponent); - comp = fixture.componentInstance; - }); - - describe('OnInit', () => { - it('Should call load all on init', () => { - // GIVEN - - // WHEN - comp.ngOnInit(); - - // THEN - expect(comp.user).toEqual( - jasmine.objectContaining({ - id: 1, - login: 'user', - firstName: 'first', - lastName: 'last', - email: 'first@last.com', - activated: true, - langKey: 'en', - authorities: ['ROLE_USER'], - createdBy: 'admin', - createdDate: null, - lastModifiedBy: null, - lastModifiedDate: null, - password: null - }) - ); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/user-management/user-management-update.component.spec.ts b/src/test/javascript/spec/app/admin/user-management/user-management-update.component.spec.ts deleted file mode 100644 index d95525af..00000000 --- a/src/test/javascript/spec/app/admin/user-management/user-management-update.component.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { ComponentFixture, TestBed, async, inject, fakeAsync, tick } from '@angular/core/testing'; -import { HttpResponse } from '@angular/common/http'; -import { ActivatedRoute } from '@angular/router'; -import { Observable, of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { UserMgmtUpdateComponent } from 'app/admin/user-management/user-management-update.component'; -import { UserService, User, JhiLanguageHelper } from 'app/core'; - -describe('Component Tests', () => { - describe('User Management Update Component', () => { - let comp: UserMgmtUpdateComponent; - let fixture: ComponentFixture; - let service: UserService; - let mockLanguageHelper: any; - const route = ({ - data: of({ user: new User(1, 'user', 'first', 'last', 'first@last.com', true, 'en', ['ROLE_USER'], 'admin', null, null, null) }) - } as any) as ActivatedRoute; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [UserMgmtUpdateComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: route - } - ] - }) - .overrideTemplate(UserMgmtUpdateComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(UserMgmtUpdateComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(UserService); - mockLanguageHelper = fixture.debugElement.injector.get(JhiLanguageHelper); - }); - - describe('OnInit', () => { - it('Should load authorities and language on init', inject( - [], - fakeAsync(() => { - // GIVEN - spyOn(service, 'authorities').and.returnValue(of(['USER'])); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.authorities).toHaveBeenCalled(); - expect(comp.authorities).toEqual(['USER']); - expect(mockLanguageHelper.getAllSpy).toHaveBeenCalled(); - }) - )); - }); - - describe('save', () => { - it('Should call update service on save for existing user', inject( - [], - fakeAsync(() => { - // GIVEN - const entity = new User(123); - spyOn(service, 'update').and.returnValue( - of( - new HttpResponse({ - body: entity - }) - ) - ); - comp.user = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.update).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - }) - )); - - it('Should call create service on save for new user', inject( - [], - fakeAsync(() => { - // GIVEN - const entity = new User(); - spyOn(service, 'create').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.user = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.create).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - }) - )); - }); - }); -}); diff --git a/src/test/javascript/spec/app/admin/user-management/user-management.component.spec.ts b/src/test/javascript/spec/app/admin/user-management/user-management.component.spec.ts deleted file mode 100644 index d30bf254..00000000 --- a/src/test/javascript/spec/app/admin/user-management/user-management.component.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { ComponentFixture, TestBed, async, inject, fakeAsync, tick } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { UserMgmtComponent } from 'app/admin/user-management/user-management.component'; -import { UserService, User } from 'app/core'; - -describe('Component Tests', () => { - describe('User Management Component', () => { - let comp: UserMgmtComponent; - let fixture: ComponentFixture; - let service: UserService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [UserMgmtComponent] - }) - .overrideTemplate(UserMgmtComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(UserMgmtComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(UserService); - }); - - describe('OnInit', () => { - it('Should call load all on init', inject( - [], - fakeAsync(() => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new User(123)], - headers - }) - ) - ); - - // WHEN - comp.ngOnInit(); - tick(); // simulate async - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.users[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }) - )); - }); - - describe('setActive', () => { - it('Should update user and call load all', inject( - [], - fakeAsync(() => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - const user = new User(123); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [user], - headers - }) - ) - ); - spyOn(service, 'update').and.returnValue(of(new HttpResponse({ status: 200 }))); - - // WHEN - comp.setActive(user, true); - tick(); // simulate async - - // THEN - expect(service.update).toHaveBeenCalledWith(user); - expect(service.query).toHaveBeenCalled(); - expect(comp.users[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }) - )); - }); - }); -}); diff --git a/src/test/javascript/spec/app/core/user/account.service.spec.ts b/src/test/javascript/spec/app/core/user/account.service.spec.ts deleted file mode 100644 index 7ed772de..00000000 --- a/src/test/javascript/spec/app/core/user/account.service.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { TestBed } from '@angular/core/testing'; -import { SERVER_API_URL } from 'app/app.constants'; -import { AccountService } from 'app/core'; -import { JhiDateUtils, JhiLanguageService } from 'ng-jhipster'; -import { SessionStorageService } from 'ngx-webstorage'; -import { MockLanguageService } from '../../../helpers/mock-language.service'; - -describe('Service Tests', () => { - describe('Account Service', () => { - let service: AccountService; - let httpMock; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - JhiDateUtils, - SessionStorageService, - { - provide: JhiLanguageService, - useClass: MockLanguageService - } - ] - }); - - service = TestBed.get(AccountService); - httpMock = TestBed.get(HttpTestingController); - }); - - afterEach(() => { - httpMock.verify(); - }); - - describe('Service methods', () => { - it('should call /account if user is undefined', () => { - service.identity().then(() => {}); - const req = httpMock.expectOne({ method: 'GET' }); - const resourceUrl = SERVER_API_URL + 'api/account'; - - expect(req.request.url).toEqual(`${resourceUrl}`); - }); - - it('should call /account only once', () => { - service.identity().then(() => service.identity().then(() => {})); - const req = httpMock.expectOne({ method: 'GET' }); - const resourceUrl = SERVER_API_URL + 'api/account'; - - expect(req.request.url).toEqual(`${resourceUrl}`); - req.flush({ - firstName: 'John' - }); - }); - - describe('hasAuthority', () => { - it('should return false if user is not logged', async () => { - const hasAuthority = await service.hasAuthority('ROLE_USER'); - expect(hasAuthority).toBeFalsy(); - }); - - it('should return false if user is logged and has not authority', async () => { - service.authenticate({ - authorities: ['ROLE_USER'] - }); - - const hasAuthority = await service.hasAuthority('ROLE_ADMIN'); - - expect(hasAuthority).toBeFalsy(); - }); - - it('should return true if user is logged and has authority', async () => { - service.authenticate({ - authorities: ['ROLE_USER'] - }); - - const hasAuthority = await service.hasAuthority('ROLE_USER'); - - expect(hasAuthority).toBeTruthy(); - }); - }); - - describe('hasAnyAuthority', () => { - it('should return false if user is not logged', async () => { - const hasAuthority = await service.hasAnyAuthority(['ROLE_USER']); - expect(hasAuthority).toBeFalsy(); - }); - - it('should return false if user is logged and has not authority', async () => { - service.authenticate({ - authorities: ['ROLE_USER'] - }); - - const hasAuthority = await service.hasAnyAuthority(['ROLE_ADMIN']); - - expect(hasAuthority).toBeFalsy(); - }); - - it('should return true if user is logged and has authority', async () => { - service.authenticate({ - authorities: ['ROLE_USER'] - }); - - const hasAuthority = await service.hasAnyAuthority(['ROLE_USER', 'ROLE_ADMIN']); - - expect(hasAuthority).toBeTruthy(); - }); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/core/user/user.service.spec.ts b/src/test/javascript/spec/app/core/user/user.service.spec.ts deleted file mode 100644 index 9c05839a..00000000 --- a/src/test/javascript/spec/app/core/user/user.service.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { JhiDateUtils } from 'ng-jhipster'; - -import { UserService, User } from 'app/core'; -import { SERVER_API_URL } from 'app/app.constants'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; - -describe('Service Tests', () => { - describe('User Service', () => { - let service: UserService; - let httpMock; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [JhiDateUtils] - }); - - service = TestBed.get(UserService); - httpMock = TestBed.get(HttpTestingController); - }); - - afterEach(() => { - httpMock.verify(); - }); - - describe('Service methods', () => { - it('should call correct URL', () => { - service.find('user').subscribe(() => {}); - - const req = httpMock.expectOne({ method: 'GET' }); - const resourceUrl = SERVER_API_URL + 'api/users'; - expect(req.request.url).toEqual(`${resourceUrl}/user`); - }); - it('should return User', () => { - service.find('user').subscribe(received => { - expect(received.body.login).toEqual('user'); - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(new User(1, 'user')); - }); - - it('should return Authorities', () => { - service.authorities().subscribe(_authorities => { - expect(_authorities).toEqual(['ROLE_USER', 'ROLE_ADMIN']); - }); - const req = httpMock.expectOne({ method: 'GET' }); - - req.flush(['ROLE_USER', 'ROLE_ADMIN']); - }); - - it('should propagate not found response', () => { - service.find('user').subscribe(null, (_error: any) => { - expect(_error.status).toEqual(404); - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush('Invalid request parameters', { - status: 404, - statusText: 'Bad Request' - }); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/asset/asset-delete-dialog.component.spec.ts b/src/test/javascript/spec/app/entities/asset/asset-delete-dialog.component.spec.ts deleted file mode 100644 index 26ab4fb0..00000000 --- a/src/test/javascript/spec/app/entities/asset/asset-delete-dialog.component.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Observable, of } from 'rxjs'; -import { JhiEventManager } from 'ng-jhipster'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { AssetDeleteDialogComponent } from 'app/entities/asset/asset-delete-dialog.component'; -import { AssetService } from 'app/entities/asset/asset.service'; - -describe('Component Tests', () => { - describe('Asset Management Delete Component', () => { - let comp: AssetDeleteDialogComponent; - let fixture: ComponentFixture; - let service: AssetService; - let mockEventManager: any; - let mockActiveModal: any; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [AssetDeleteDialogComponent] - }) - .overrideTemplate(AssetDeleteDialogComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(AssetDeleteDialogComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(AssetService); - mockEventManager = fixture.debugElement.injector.get(JhiEventManager); - mockActiveModal = fixture.debugElement.injector.get(NgbActiveModal); - }); - - describe('confirmDelete', () => { - it('Should call delete service on confirmDelete', inject( - [], - fakeAsync(() => { - // GIVEN - spyOn(service, 'delete').and.returnValue(of({})); - - // WHEN - comp.confirmDelete(123); - tick(); - - // THEN - expect(service.delete).toHaveBeenCalledWith(123); - expect(mockActiveModal.dismissSpy).toHaveBeenCalled(); - expect(mockEventManager.broadcastSpy).toHaveBeenCalled(); - }) - )); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/asset/asset-detail.component.spec.ts b/src/test/javascript/spec/app/entities/asset/asset-detail.component.spec.ts deleted file mode 100644 index 8d176985..00000000 --- a/src/test/javascript/spec/app/entities/asset/asset-detail.component.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { AssetDetailComponent } from 'app/entities/asset/asset-detail.component'; -import { Asset } from 'app/shared/model/asset.model'; - -describe('Component Tests', () => { - describe('Asset Management Detail Component', () => { - let comp: AssetDetailComponent; - let fixture: ComponentFixture; - const route = ({ data: of({ asset: new Asset(123) }) } as any) as ActivatedRoute; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [AssetDetailComponent], - providers: [{ provide: ActivatedRoute, useValue: route }] - }) - .overrideTemplate(AssetDetailComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(AssetDetailComponent); - comp = fixture.componentInstance; - }); - - describe('OnInit', () => { - it('Should call load all on init', () => { - // GIVEN - - // WHEN - comp.ngOnInit(); - - // THEN - expect(comp.asset).toEqual(jasmine.objectContaining({ id: 123 })); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/asset/asset-update.component.spec.ts b/src/test/javascript/spec/app/entities/asset/asset-update.component.spec.ts deleted file mode 100644 index 20bcdf53..00000000 --- a/src/test/javascript/spec/app/entities/asset/asset-update.component.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { HttpResponse } from '@angular/common/http'; -import { Observable, of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { AssetUpdateComponent } from 'app/entities/asset/asset-update.component'; -import { AssetService } from 'app/entities/asset/asset.service'; -import { Asset } from 'app/shared/model/asset.model'; - -describe('Component Tests', () => { - describe('Asset Management Update Component', () => { - let comp: AssetUpdateComponent; - let fixture: ComponentFixture; - let service: AssetService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [AssetUpdateComponent] - }) - .overrideTemplate(AssetUpdateComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(AssetUpdateComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(AssetService); - }); - - describe('save', () => { - it('Should call update service on save for existing entity', fakeAsync(() => { - // GIVEN - const entity = new Asset(123); - spyOn(service, 'update').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.asset = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.update).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - - it('Should call create service on save for new entity', fakeAsync(() => { - // GIVEN - const entity = new Asset(); - spyOn(service, 'create').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.asset = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.create).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/asset/asset.component.spec.ts b/src/test/javascript/spec/app/entities/asset/asset.component.spec.ts deleted file mode 100644 index c8afd397..00000000 --- a/src/test/javascript/spec/app/entities/asset/asset.component.spec.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute, Data } from '@angular/router'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { AssetComponent } from 'app/entities/asset/asset.component'; -import { AssetService } from 'app/entities/asset/asset.service'; -import { Asset } from 'app/shared/model/asset.model'; - -describe('Component Tests', () => { - describe('Asset Management Component', () => { - let comp: AssetComponent; - let fixture: ComponentFixture; - let service: AssetService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [AssetComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: { - data: { - subscribe: (fn: (value: Data) => void) => - fn({ - pagingParams: { - predicate: 'id', - reverse: false, - page: 0 - } - }) - } - } - } - ] - }) - .overrideTemplate(AssetComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(AssetComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(AssetService); - }); - - it('Should call load all on init', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Asset(123)], - headers - }) - ) - ); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.assets[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should load a page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Asset(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.assets[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should re-initialize the page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Asset(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - comp.reset(); - - // THEN - expect(comp.page).toEqual(0); - expect(service.query).toHaveBeenCalledTimes(2); - expect(comp.assets[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - it('should calculate the sort attribute for an id', () => { - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['id,asc']); - }); - - it('should calculate the sort attribute for a non-id attribute', () => { - // GIVEN - comp.predicate = 'name'; - - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['name,asc', 'id']); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/asset/asset.service.spec.ts b/src/test/javascript/spec/app/entities/asset/asset.service.spec.ts deleted file mode 100644 index 28250778..00000000 --- a/src/test/javascript/spec/app/entities/asset/asset.service.spec.ts +++ /dev/null @@ -1,142 +0,0 @@ -/* tslint:disable max-line-length */ -import { TestBed, getTestBed } from '@angular/core/testing'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { of } from 'rxjs'; -import { take, map } from 'rxjs/operators'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { AssetService } from 'app/entities/asset/asset.service'; -import { IAsset, Asset, AssetAction } from 'app/shared/model/asset.model'; - -describe('Service Tests', () => { - describe('Asset Service', () => { - let injector: TestBed; - let service: AssetService; - let httpMock: HttpTestingController; - let elemDefault: IAsset; - let currentDate: moment.Moment; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - injector = getTestBed(); - service = injector.get(AssetService); - httpMock = injector.get(HttpTestingController); - currentDate = moment(); - - elemDefault = new Asset(0, currentDate, currentDate, AssetAction.PAYMENT, 0, 'AAAAAAA'); - }); - - describe('Service methods', async () => { - it('should find an element', async () => { - const returnedFromService = Object.assign( - { - documentDate: currentDate.format(DATE_FORMAT), - valueDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - service - .find(123) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: elemDefault })); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should create a Asset', async () => { - const returnedFromService = Object.assign( - { - id: 0, - documentDate: currentDate.format(DATE_FORMAT), - valueDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - const expected = Object.assign( - { - documentDate: currentDate, - valueDate: currentDate - }, - returnedFromService - ); - service - .create(new Asset(null)) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'POST' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should update a Asset', async () => { - const returnedFromService = Object.assign( - { - documentDate: currentDate.format(DATE_FORMAT), - valueDate: currentDate.format(DATE_FORMAT), - action: 'BBBBBB', - amount: 1, - remark: 'BBBBBB' - }, - elemDefault - ); - - const expected = Object.assign( - { - documentDate: currentDate, - valueDate: currentDate - }, - returnedFromService - ); - service - .update(expected) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'PUT' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should return a list of Asset', async () => { - const returnedFromService = Object.assign( - { - documentDate: currentDate.format(DATE_FORMAT), - valueDate: currentDate.format(DATE_FORMAT), - action: 'BBBBBB', - amount: 1, - remark: 'BBBBBB' - }, - elemDefault - ); - const expected = Object.assign( - { - documentDate: currentDate, - valueDate: currentDate - }, - returnedFromService - ); - service - .query(expected) - .pipe( - take(1), - map(resp => resp.body) - ) - .subscribe(body => expect(body).toContainEqual(expected)); - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify([returnedFromService])); - httpMock.verify(); - }); - - it('should delete a Asset', async () => { - const rxPromise = service.delete(123).subscribe(resp => expect(resp.ok)); - - const req = httpMock.expectOne({ method: 'DELETE' }); - req.flush({ status: 200 }); - }); - }); - - afterEach(() => { - httpMock.verify(); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/customer/customer-delete-dialog.component.spec.ts b/src/test/javascript/spec/app/entities/customer/customer-delete-dialog.component.spec.ts deleted file mode 100644 index 7b4f83cc..00000000 --- a/src/test/javascript/spec/app/entities/customer/customer-delete-dialog.component.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Observable, of } from 'rxjs'; -import { JhiEventManager } from 'ng-jhipster'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { CustomerDeleteDialogComponent } from 'app/entities/customer/customer-delete-dialog.component'; -import { CustomerService } from 'app/entities/customer/customer.service'; - -describe('Component Tests', () => { - describe('Customer Management Delete Component', () => { - let comp: CustomerDeleteDialogComponent; - let fixture: ComponentFixture; - let service: CustomerService; - let mockEventManager: any; - let mockActiveModal: any; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [CustomerDeleteDialogComponent] - }) - .overrideTemplate(CustomerDeleteDialogComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(CustomerDeleteDialogComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(CustomerService); - mockEventManager = fixture.debugElement.injector.get(JhiEventManager); - mockActiveModal = fixture.debugElement.injector.get(NgbActiveModal); - }); - - describe('confirmDelete', () => { - it('Should call delete service on confirmDelete', inject( - [], - fakeAsync(() => { - // GIVEN - spyOn(service, 'delete').and.returnValue(of({})); - - // WHEN - comp.confirmDelete(123); - tick(); - - // THEN - expect(service.delete).toHaveBeenCalledWith(123); - expect(mockActiveModal.dismissSpy).toHaveBeenCalled(); - expect(mockEventManager.broadcastSpy).toHaveBeenCalled(); - }) - )); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/customer/customer-detail.component.spec.ts b/src/test/javascript/spec/app/entities/customer/customer-detail.component.spec.ts deleted file mode 100644 index 1e4506ac..00000000 --- a/src/test/javascript/spec/app/entities/customer/customer-detail.component.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { CustomerDetailComponent } from 'app/entities/customer/customer-detail.component'; -import { Customer } from 'app/shared/model/customer.model'; - -describe('Component Tests', () => { - describe('Customer Management Detail Component', () => { - let comp: CustomerDetailComponent; - let fixture: ComponentFixture; - const route = ({ data: of({ customer: new Customer(123) }) } as any) as ActivatedRoute; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [CustomerDetailComponent], - providers: [{ provide: ActivatedRoute, useValue: route }] - }) - .overrideTemplate(CustomerDetailComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(CustomerDetailComponent); - comp = fixture.componentInstance; - }); - - describe('OnInit', () => { - it('Should call load all on init', () => { - // GIVEN - - // WHEN - comp.ngOnInit(); - - // THEN - expect(comp.customer).toEqual(jasmine.objectContaining({ id: 123 })); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/customer/customer-update.component.spec.ts b/src/test/javascript/spec/app/entities/customer/customer-update.component.spec.ts deleted file mode 100644 index e50a46df..00000000 --- a/src/test/javascript/spec/app/entities/customer/customer-update.component.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { HttpResponse } from '@angular/common/http'; -import { Observable, of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { CustomerUpdateComponent } from 'app/entities/customer/customer-update.component'; -import { CustomerService } from 'app/entities/customer/customer.service'; -import { Customer } from 'app/shared/model/customer.model'; - -describe('Component Tests', () => { - describe('Customer Management Update Component', () => { - let comp: CustomerUpdateComponent; - let fixture: ComponentFixture; - let service: CustomerService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [CustomerUpdateComponent] - }) - .overrideTemplate(CustomerUpdateComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(CustomerUpdateComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(CustomerService); - }); - - describe('save', () => { - it('Should call update service on save for existing entity', fakeAsync(() => { - // GIVEN - const entity = new Customer(123); - spyOn(service, 'update').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.customer = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.update).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - - it('Should call create service on save for new entity', fakeAsync(() => { - // GIVEN - const entity = new Customer(); - spyOn(service, 'create').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.customer = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.create).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/customer/customer.component.spec.ts b/src/test/javascript/spec/app/entities/customer/customer.component.spec.ts deleted file mode 100644 index 414db92f..00000000 --- a/src/test/javascript/spec/app/entities/customer/customer.component.spec.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute, Data } from '@angular/router'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { CustomerComponent } from 'app/entities/customer/customer.component'; -import { CustomerService } from 'app/entities/customer/customer.service'; -import { Customer } from 'app/shared/model/customer.model'; - -describe('Component Tests', () => { - describe('Customer Management Component', () => { - let comp: CustomerComponent; - let fixture: ComponentFixture; - let service: CustomerService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [CustomerComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: { - data: { - subscribe: (fn: (value: Data) => void) => - fn({ - pagingParams: { - predicate: 'id', - reverse: false, - page: 0 - } - }) - } - } - } - ] - }) - .overrideTemplate(CustomerComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(CustomerComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(CustomerService); - }); - - it('Should call load all on init', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Customer(123)], - headers - }) - ) - ); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.customers[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should load a page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Customer(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.customers[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should re-initialize the page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Customer(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - comp.reset(); - - // THEN - expect(comp.page).toEqual(0); - expect(service.query).toHaveBeenCalledTimes(2); - expect(comp.customers[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - it('should calculate the sort attribute for an id', () => { - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['id,asc']); - }); - - it('should calculate the sort attribute for a non-id attribute', () => { - // GIVEN - comp.predicate = 'name'; - - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['name,asc', 'id']); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/customer/customer.service.spec.ts b/src/test/javascript/spec/app/entities/customer/customer.service.spec.ts deleted file mode 100644 index 6d717639..00000000 --- a/src/test/javascript/spec/app/entities/customer/customer.service.spec.ts +++ /dev/null @@ -1,172 +0,0 @@ -/* tslint:disable max-line-length */ -import { getTestBed, TestBed } from '@angular/core/testing'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { map, take } from 'rxjs/operators'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { CustomerService } from 'app/entities/customer/customer.service'; -import { Customer, CustomerKind, ICustomer, VatRegion } from 'app/shared/model/customer.model'; - -describe('Service Tests', () => { - describe('Customer Service', () => { - let injector: TestBed; - let service: CustomerService; - let httpMock: HttpTestingController; - let elemDefault: ICustomer; - let currentDate: moment.Moment; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - injector = getTestBed(); - service = injector.get(CustomerService); - httpMock = injector.get(HttpTestingController); - currentDate = moment(); - - elemDefault = new Customer( - 0, - 0, - 'AAAAAAA', - 'AAAAAAA', - CustomerKind.NATURAL, - currentDate, - 'AAAAAAA', - 'AAAAAAA', - 'AAAAAAA', - VatRegion.DOMESTIC, - 'AAAAAAA', - 'AAAAAAA', - 'AAAAAAA', - 'AAAAAAA', - 'AAAAAAA', - 'AAAAAAA' - ); - }); - - describe('Service methods', async () => { - it('should find an element', async () => { - const returnedFromService = Object.assign( - { - birthDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - service - .find(123) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: elemDefault })); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should create a Customer', async () => { - const returnedFromService = Object.assign( - { - id: 0, - birthDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - const expected = Object.assign( - { - birthDate: currentDate - }, - returnedFromService - ); - service - .create(new Customer(null)) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'POST' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should update a Customer', async () => { - const returnedFromService = Object.assign( - { - reference: 1, - prefix: 'BBBBBB', - name: 'BBBBBB', - kind: 'BBBBBB', - birthDate: currentDate.format(DATE_FORMAT), - birthPlace: 'BBBBBB', - registrationCourt: 'BBBBBB', - registrationNumber: 'BBBBBB', - vatRegion: 'BBBBBB', - vatNumber: 'BBBBBB', - contractualSalutation: 'BBBBBB', - contractualAddress: 'BBBBBB', - billingSalutation: 'BBBBBB', - billingAddress: 'BBBBBB', - remark: 'BBBBBB' - }, - elemDefault - ); - - const expected = Object.assign( - { - birthDate: currentDate - }, - returnedFromService - ); - service - .update(expected) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'PUT' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should return a list of Customer', async () => { - const returnedFromService = Object.assign( - { - reference: 1, - prefix: 'BBBBBB', - name: 'BBBBBB', - kind: 'BBBBBB', - birthDate: currentDate.format(DATE_FORMAT), - birthPlace: 'BBBBBB', - registrationCourt: 'BBBBBB', - registrationNumber: 'BBBBBB', - vatRegion: 'BBBBBB', - vatNumber: 'BBBBBB', - contractualSalutation: 'BBBBBB', - contractualAddress: 'BBBBBB', - billingSalutation: 'BBBBBB', - billingAddress: 'BBBBBB', - remark: 'BBBBBB' - }, - elemDefault - ); - const expected = Object.assign( - { - birthDate: currentDate - }, - returnedFromService - ); - service - .query(expected) - .pipe( - take(1), - map(resp => resp.body) - ) - .subscribe(body => expect(body).toContainEqual(expected)); - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify([returnedFromService])); - httpMock.verify(); - }); - - it('should delete a Customer', async () => { - const rxPromise = service.delete(123).subscribe(resp => expect(resp.ok)); - - const req = httpMock.expectOne({ method: 'DELETE' }); - req.flush({ status: 200 }); - }); - }); - - afterEach(() => { - httpMock.verify(); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/membership/membership-delete-dialog.component.spec.ts b/src/test/javascript/spec/app/entities/membership/membership-delete-dialog.component.spec.ts deleted file mode 100644 index 9b396b77..00000000 --- a/src/test/javascript/spec/app/entities/membership/membership-delete-dialog.component.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Observable, of } from 'rxjs'; -import { JhiEventManager } from 'ng-jhipster'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { MembershipDeleteDialogComponent } from 'app/entities/membership/membership-delete-dialog.component'; -import { MembershipService } from 'app/entities/membership/membership.service'; - -describe('Component Tests', () => { - describe('Membership Management Delete Component', () => { - let comp: MembershipDeleteDialogComponent; - let fixture: ComponentFixture; - let service: MembershipService; - let mockEventManager: any; - let mockActiveModal: any; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [MembershipDeleteDialogComponent] - }) - .overrideTemplate(MembershipDeleteDialogComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(MembershipDeleteDialogComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(MembershipService); - mockEventManager = fixture.debugElement.injector.get(JhiEventManager); - mockActiveModal = fixture.debugElement.injector.get(NgbActiveModal); - }); - - describe('confirmDelete', () => { - it('Should call delete service on confirmDelete', inject( - [], - fakeAsync(() => { - // GIVEN - spyOn(service, 'delete').and.returnValue(of({})); - - // WHEN - comp.confirmDelete(123); - tick(); - - // THEN - expect(service.delete).toHaveBeenCalledWith(123); - expect(mockActiveModal.dismissSpy).toHaveBeenCalled(); - expect(mockEventManager.broadcastSpy).toHaveBeenCalled(); - }) - )); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/membership/membership-detail.component.spec.ts b/src/test/javascript/spec/app/entities/membership/membership-detail.component.spec.ts deleted file mode 100644 index a66f96f5..00000000 --- a/src/test/javascript/spec/app/entities/membership/membership-detail.component.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { MembershipDetailComponent } from 'app/entities/membership/membership-detail.component'; -import { Membership } from 'app/shared/model/membership.model'; - -describe('Component Tests', () => { - describe('Membership Management Detail Component', () => { - let comp: MembershipDetailComponent; - let fixture: ComponentFixture; - const route = ({ data: of({ membership: new Membership(123) }) } as any) as ActivatedRoute; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [MembershipDetailComponent], - providers: [{ provide: ActivatedRoute, useValue: route }] - }) - .overrideTemplate(MembershipDetailComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(MembershipDetailComponent); - comp = fixture.componentInstance; - }); - - describe('OnInit', () => { - it('Should call load all on init', () => { - // GIVEN - - // WHEN - comp.ngOnInit(); - - // THEN - expect(comp.membership).toEqual(jasmine.objectContaining({ id: 123 })); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/membership/membership-update.component.spec.ts b/src/test/javascript/spec/app/entities/membership/membership-update.component.spec.ts deleted file mode 100644 index abd5a5a2..00000000 --- a/src/test/javascript/spec/app/entities/membership/membership-update.component.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { HttpResponse } from '@angular/common/http'; -import { Observable, of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { MembershipUpdateComponent } from 'app/entities/membership/membership-update.component'; -import { MembershipService } from 'app/entities/membership/membership.service'; -import { Membership } from 'app/shared/model/membership.model'; - -describe('Component Tests', () => { - describe('Membership Management Update Component', () => { - let comp: MembershipUpdateComponent; - let fixture: ComponentFixture; - let service: MembershipService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [MembershipUpdateComponent] - }) - .overrideTemplate(MembershipUpdateComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(MembershipUpdateComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(MembershipService); - }); - - describe('save', () => { - it('Should call update service on save for existing entity', fakeAsync(() => { - // GIVEN - const entity = new Membership(123); - spyOn(service, 'update').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.membership = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.update).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - - it('Should call create service on save for new entity', fakeAsync(() => { - // GIVEN - const entity = new Membership(); - spyOn(service, 'create').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.membership = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.create).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/membership/membership.component.spec.ts b/src/test/javascript/spec/app/entities/membership/membership.component.spec.ts deleted file mode 100644 index a43d6a1c..00000000 --- a/src/test/javascript/spec/app/entities/membership/membership.component.spec.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute, Data } from '@angular/router'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { MembershipComponent } from 'app/entities/membership/membership.component'; -import { MembershipService } from 'app/entities/membership/membership.service'; -import { Membership } from 'app/shared/model/membership.model'; - -describe('Component Tests', () => { - describe('Membership Management Component', () => { - let comp: MembershipComponent; - let fixture: ComponentFixture; - let service: MembershipService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [MembershipComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: { - data: { - subscribe: (fn: (value: Data) => void) => - fn({ - pagingParams: { - predicate: 'id', - reverse: false, - page: 0 - } - }) - } - } - } - ] - }) - .overrideTemplate(MembershipComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(MembershipComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(MembershipService); - }); - - it('Should call load all on init', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Membership(123)], - headers - }) - ) - ); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.memberships[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should load a page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Membership(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.memberships[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should re-initialize the page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Membership(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - comp.reset(); - - // THEN - expect(comp.page).toEqual(0); - expect(service.query).toHaveBeenCalledTimes(2); - expect(comp.memberships[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - it('should calculate the sort attribute for an id', () => { - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['id,asc']); - }); - - it('should calculate the sort attribute for a non-id attribute', () => { - // GIVEN - comp.predicate = 'name'; - - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['name,asc', 'id']); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/membership/membership.service.spec.ts b/src/test/javascript/spec/app/entities/membership/membership.service.spec.ts deleted file mode 100644 index 1f5ced40..00000000 --- a/src/test/javascript/spec/app/entities/membership/membership.service.spec.ts +++ /dev/null @@ -1,150 +0,0 @@ -/* tslint:disable max-line-length */ -import { getTestBed, TestBed } from '@angular/core/testing'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { map, take } from 'rxjs/operators'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { MembershipService } from 'app/entities/membership/membership.service'; -import { IMembership, Membership } from 'app/shared/model/membership.model'; - -describe('Service Tests', () => { - describe('Membership Service', () => { - let injector: TestBed; - let service: MembershipService; - let httpMock: HttpTestingController; - let elemDefault: IMembership; - let currentDate: moment.Moment; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - injector = getTestBed(); - service = injector.get(MembershipService); - httpMock = injector.get(HttpTestingController); - currentDate = moment(); - - elemDefault = new Membership(0, currentDate, currentDate, currentDate, currentDate, 'AAAAAAA'); - }); - - describe('Service methods', async () => { - it('should find an element', async () => { - const returnedFromService = Object.assign( - { - admissionDocumentDate: currentDate.format(DATE_FORMAT), - cancellationDocumentDate: currentDate.format(DATE_FORMAT), - memberFromDate: currentDate.format(DATE_FORMAT), - memberUntilDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - service - .find(123) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: elemDefault })); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should create a Membership', async () => { - const returnedFromService = Object.assign( - { - id: 0, - admissionDocumentDate: currentDate.format(DATE_FORMAT), - cancellationDocumentDate: currentDate.format(DATE_FORMAT), - memberFromDate: currentDate.format(DATE_FORMAT), - memberUntilDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - const expected = Object.assign( - { - admissionDocumentDate: currentDate, - cancellationDocumentDate: currentDate, - memberFromDate: currentDate, - memberUntilDate: currentDate - }, - returnedFromService - ); - service - .create(new Membership(null)) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'POST' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should update a Membership', async () => { - const returnedFromService = Object.assign( - { - admissionDocumentDate: currentDate.format(DATE_FORMAT), - cancellationDocumentDate: currentDate.format(DATE_FORMAT), - memberFromDate: currentDate.format(DATE_FORMAT), - memberUntilDate: currentDate.format(DATE_FORMAT), - remark: 'BBBBBB' - }, - elemDefault - ); - - const expected = Object.assign( - { - admissionDocumentDate: currentDate, - cancellationDocumentDate: currentDate, - memberFromDate: currentDate, - memberUntilDate: currentDate - }, - returnedFromService - ); - service - .update(expected) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'PUT' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should return a list of Membership', async () => { - const returnedFromService = Object.assign( - { - admissionDocumentDate: currentDate.format(DATE_FORMAT), - cancellationDocumentDate: currentDate.format(DATE_FORMAT), - memberFromDate: currentDate.format(DATE_FORMAT), - memberUntilDate: currentDate.format(DATE_FORMAT), - remark: 'BBBBBB' - }, - elemDefault - ); - const expected = Object.assign( - { - admissionDocumentDate: currentDate, - cancellationDocumentDate: currentDate, - memberFromDate: currentDate, - memberUntilDate: currentDate - }, - returnedFromService - ); - service - .query(expected) - .pipe( - take(1), - map(resp => resp.body) - ) - .subscribe(body => expect(body).toContainEqual(expected)); - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify([returnedFromService])); - httpMock.verify(); - }); - - it('should delete a Membership', async () => { - const rxPromise = service.delete(123).subscribe(resp => expect(resp.ok)); - - const req = httpMock.expectOne({ method: 'DELETE' }); - req.flush({ status: 200 }); - }); - }); - - afterEach(() => { - httpMock.verify(); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.spec.ts b/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.spec.ts deleted file mode 100644 index 6dec0e0e..00000000 --- a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-delete-dialog.component.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Observable, of } from 'rxjs'; -import { JhiEventManager } from 'ng-jhipster'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { SepaMandateDeleteDialogComponent } from 'app/entities/sepa-mandate/sepa-mandate-delete-dialog.component'; -import { SepaMandateService } from 'app/entities/sepa-mandate/sepa-mandate.service'; - -describe('Component Tests', () => { - describe('SepaMandate Management Delete Component', () => { - let comp: SepaMandateDeleteDialogComponent; - let fixture: ComponentFixture; - let service: SepaMandateService; - let mockEventManager: any; - let mockActiveModal: any; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [SepaMandateDeleteDialogComponent] - }) - .overrideTemplate(SepaMandateDeleteDialogComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(SepaMandateDeleteDialogComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(SepaMandateService); - mockEventManager = fixture.debugElement.injector.get(JhiEventManager); - mockActiveModal = fixture.debugElement.injector.get(NgbActiveModal); - }); - - describe('confirmDelete', () => { - it('Should call delete service on confirmDelete', inject( - [], - fakeAsync(() => { - // GIVEN - spyOn(service, 'delete').and.returnValue(of({})); - - // WHEN - comp.confirmDelete(123); - tick(); - - // THEN - expect(service.delete).toHaveBeenCalledWith(123); - expect(mockActiveModal.dismissSpy).toHaveBeenCalled(); - expect(mockEventManager.broadcastSpy).toHaveBeenCalled(); - }) - )); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-detail.component.spec.ts b/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-detail.component.spec.ts deleted file mode 100644 index ba8ffcb2..00000000 --- a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-detail.component.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { SepaMandateDetailComponent } from 'app/entities/sepa-mandate/sepa-mandate-detail.component'; -import { SepaMandate } from 'app/shared/model/sepa-mandate.model'; - -describe('Component Tests', () => { - describe('SepaMandate Management Detail Component', () => { - let comp: SepaMandateDetailComponent; - let fixture: ComponentFixture; - const route = ({ data: of({ sepaMandate: new SepaMandate(123) }) } as any) as ActivatedRoute; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [SepaMandateDetailComponent], - providers: [{ provide: ActivatedRoute, useValue: route }] - }) - .overrideTemplate(SepaMandateDetailComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(SepaMandateDetailComponent); - comp = fixture.componentInstance; - }); - - describe('OnInit', () => { - it('Should call load all on init', () => { - // GIVEN - - // WHEN - comp.ngOnInit(); - - // THEN - expect(comp.sepaMandate).toEqual(jasmine.objectContaining({ id: 123 })); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-update.component.spec.ts b/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-update.component.spec.ts deleted file mode 100644 index c2246524..00000000 --- a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate-update.component.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { HttpResponse } from '@angular/common/http'; -import { Observable, of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { SepaMandateUpdateComponent } from 'app/entities/sepa-mandate/sepa-mandate-update.component'; -import { SepaMandateService } from 'app/entities/sepa-mandate/sepa-mandate.service'; -import { SepaMandate } from 'app/shared/model/sepa-mandate.model'; - -describe('Component Tests', () => { - describe('SepaMandate Management Update Component', () => { - let comp: SepaMandateUpdateComponent; - let fixture: ComponentFixture; - let service: SepaMandateService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [SepaMandateUpdateComponent] - }) - .overrideTemplate(SepaMandateUpdateComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(SepaMandateUpdateComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(SepaMandateService); - }); - - describe('save', () => { - it('Should call update service on save for existing entity', fakeAsync(() => { - // GIVEN - const entity = new SepaMandate(123); - spyOn(service, 'update').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.sepaMandate = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.update).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - - it('Should call create service on save for new entity', fakeAsync(() => { - // GIVEN - const entity = new SepaMandate(); - spyOn(service, 'create').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.sepaMandate = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.create).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate.component.spec.ts b/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate.component.spec.ts deleted file mode 100644 index 66ce50bd..00000000 --- a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate.component.spec.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute, Data } from '@angular/router'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { SepaMandateComponent } from 'app/entities/sepa-mandate/sepa-mandate.component'; -import { SepaMandateService } from 'app/entities/sepa-mandate/sepa-mandate.service'; -import { SepaMandate } from 'app/shared/model/sepa-mandate.model'; - -describe('Component Tests', () => { - describe('SepaMandate Management Component', () => { - let comp: SepaMandateComponent; - let fixture: ComponentFixture; - let service: SepaMandateService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [SepaMandateComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: { - data: { - subscribe: (fn: (value: Data) => void) => - fn({ - pagingParams: { - predicate: 'id', - reverse: false, - page: 0 - } - }) - } - } - } - ] - }) - .overrideTemplate(SepaMandateComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(SepaMandateComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(SepaMandateService); - }); - - it('Should call load all on init', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new SepaMandate(123)], - headers - }) - ) - ); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.sepaMandates[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should load a page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new SepaMandate(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.sepaMandates[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should re-initialize the page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new SepaMandate(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - comp.reset(); - - // THEN - expect(comp.page).toEqual(0); - expect(service.query).toHaveBeenCalledTimes(2); - expect(comp.sepaMandates[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - it('should calculate the sort attribute for an id', () => { - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['id,asc']); - }); - - it('should calculate the sort attribute for a non-id attribute', () => { - // GIVEN - comp.predicate = 'name'; - - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['name,asc', 'id']); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate.service.spec.ts b/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate.service.spec.ts deleted file mode 100644 index 89b1dc0b..00000000 --- a/src/test/javascript/spec/app/entities/sepa-mandate/sepa-mandate.service.spec.ts +++ /dev/null @@ -1,174 +0,0 @@ -/* tslint:disable max-line-length */ -import { getTestBed, TestBed } from '@angular/core/testing'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { map, take } from 'rxjs/operators'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { SepaMandateService } from 'app/entities/sepa-mandate/sepa-mandate.service'; -import { ISepaMandate, SepaMandate } from 'app/shared/model/sepa-mandate.model'; - -describe('Service Tests', () => { - describe('SepaMandate Service', () => { - let injector: TestBed; - let service: SepaMandateService; - let httpMock: HttpTestingController; - let elemDefault: ISepaMandate; - let currentDate: moment.Moment; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - injector = getTestBed(); - service = injector.get(SepaMandateService); - httpMock = injector.get(HttpTestingController); - currentDate = moment(); - - elemDefault = new SepaMandate( - 0, - 'AAAAAAA', - 'AAAAAAA', - 'AAAAAAA', - currentDate, - currentDate, - currentDate, - currentDate, - currentDate, - 'AAAAAAA' - ); - }); - - describe('Service methods', async () => { - it('should find an element', async () => { - const returnedFromService = Object.assign( - { - grantingDocumentDate: currentDate.format(DATE_FORMAT), - revokationDocumentDate: currentDate.format(DATE_FORMAT), - validFromDate: currentDate.format(DATE_FORMAT), - validUntilDate: currentDate.format(DATE_FORMAT), - lastUsedDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - service - .find(123) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: elemDefault })); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should create a SepaMandate', async () => { - const returnedFromService = Object.assign( - { - id: 0, - grantingDocumentDate: currentDate.format(DATE_FORMAT), - revokationDocumentDate: currentDate.format(DATE_FORMAT), - validFromDate: currentDate.format(DATE_FORMAT), - validUntilDate: currentDate.format(DATE_FORMAT), - lastUsedDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - const expected = Object.assign( - { - grantingDocumentDate: currentDate, - revokationDocumentDate: currentDate, - validFromDate: currentDate, - validUntilDate: currentDate, - lastUsedDate: currentDate - }, - returnedFromService - ); - service - .create(new SepaMandate(null)) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'POST' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should update a SepaMandate', async () => { - const returnedFromService = Object.assign( - { - reference: 'BBBBBB', - iban: 'BBBBBB', - bic: 'BBBBBB', - grantingDocumentDate: currentDate.format(DATE_FORMAT), - revokationDocumentDate: currentDate.format(DATE_FORMAT), - validFromDate: currentDate.format(DATE_FORMAT), - validUntilDate: currentDate.format(DATE_FORMAT), - lastUsedDate: currentDate.format(DATE_FORMAT), - remark: 'BBBBBB' - }, - elemDefault - ); - - const expected = Object.assign( - { - grantingDocumentDate: currentDate, - revokationDocumentDate: currentDate, - validFromDate: currentDate, - validUntilDate: currentDate, - lastUsedDate: currentDate - }, - returnedFromService - ); - service - .update(expected) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'PUT' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should return a list of SepaMandate', async () => { - const returnedFromService = Object.assign( - { - reference: 'BBBBBB', - iban: 'BBBBBB', - bic: 'BBBBBB', - grantingDocumentDate: currentDate.format(DATE_FORMAT), - revokationDocumentDate: currentDate.format(DATE_FORMAT), - validFromDate: currentDate.format(DATE_FORMAT), - validUntilDate: currentDate.format(DATE_FORMAT), - lastUsedDate: currentDate.format(DATE_FORMAT), - remark: 'BBBBBB' - }, - elemDefault - ); - const expected = Object.assign( - { - grantingDocumentDate: currentDate, - revokationDocumentDate: currentDate, - validFromDate: currentDate, - validUntilDate: currentDate, - lastUsedDate: currentDate - }, - returnedFromService - ); - service - .query(expected) - .pipe( - take(1), - map(resp => resp.body) - ) - .subscribe(body => expect(body).toContainEqual(expected)); - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify([returnedFromService])); - httpMock.verify(); - }); - - it('should delete a SepaMandate', async () => { - const rxPromise = service.delete(123).subscribe(resp => expect(resp.ok)); - - const req = httpMock.expectOne({ method: 'DELETE' }); - req.flush({ status: 200 }); - }); - }); - - afterEach(() => { - httpMock.verify(); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/share/share-delete-dialog.component.spec.ts b/src/test/javascript/spec/app/entities/share/share-delete-dialog.component.spec.ts deleted file mode 100644 index 7e1c8acb..00000000 --- a/src/test/javascript/spec/app/entities/share/share-delete-dialog.component.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Observable, of } from 'rxjs'; -import { JhiEventManager } from 'ng-jhipster'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { ShareDeleteDialogComponent } from 'app/entities/share/share-delete-dialog.component'; -import { ShareService } from 'app/entities/share/share.service'; - -describe('Component Tests', () => { - describe('Share Management Delete Component', () => { - let comp: ShareDeleteDialogComponent; - let fixture: ComponentFixture; - let service: ShareService; - let mockEventManager: any; - let mockActiveModal: any; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [ShareDeleteDialogComponent] - }) - .overrideTemplate(ShareDeleteDialogComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(ShareDeleteDialogComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(ShareService); - mockEventManager = fixture.debugElement.injector.get(JhiEventManager); - mockActiveModal = fixture.debugElement.injector.get(NgbActiveModal); - }); - - describe('confirmDelete', () => { - it('Should call delete service on confirmDelete', inject( - [], - fakeAsync(() => { - // GIVEN - spyOn(service, 'delete').and.returnValue(of({})); - - // WHEN - comp.confirmDelete(123); - tick(); - - // THEN - expect(service.delete).toHaveBeenCalledWith(123); - expect(mockActiveModal.dismissSpy).toHaveBeenCalled(); - expect(mockEventManager.broadcastSpy).toHaveBeenCalled(); - }) - )); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/share/share-detail.component.spec.ts b/src/test/javascript/spec/app/entities/share/share-detail.component.spec.ts deleted file mode 100644 index 69de2445..00000000 --- a/src/test/javascript/spec/app/entities/share/share-detail.component.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { ShareDetailComponent } from 'app/entities/share/share-detail.component'; -import { Share } from 'app/shared/model/share.model'; - -describe('Component Tests', () => { - describe('Share Management Detail Component', () => { - let comp: ShareDetailComponent; - let fixture: ComponentFixture; - const route = ({ data: of({ share: new Share(123) }) } as any) as ActivatedRoute; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [ShareDetailComponent], - providers: [{ provide: ActivatedRoute, useValue: route }] - }) - .overrideTemplate(ShareDetailComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(ShareDetailComponent); - comp = fixture.componentInstance; - }); - - describe('OnInit', () => { - it('Should call load all on init', () => { - // GIVEN - - // WHEN - comp.ngOnInit(); - - // THEN - expect(comp.share).toEqual(jasmine.objectContaining({ id: 123 })); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/share/share-update.component.spec.ts b/src/test/javascript/spec/app/entities/share/share-update.component.spec.ts deleted file mode 100644 index 279be464..00000000 --- a/src/test/javascript/spec/app/entities/share/share-update.component.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { HttpResponse } from '@angular/common/http'; -import { Observable, of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { ShareUpdateComponent } from 'app/entities/share/share-update.component'; -import { ShareService } from 'app/entities/share/share.service'; -import { Share } from 'app/shared/model/share.model'; - -describe('Component Tests', () => { - describe('Share Management Update Component', () => { - let comp: ShareUpdateComponent; - let fixture: ComponentFixture; - let service: ShareService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [ShareUpdateComponent] - }) - .overrideTemplate(ShareUpdateComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(ShareUpdateComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(ShareService); - }); - - describe('save', () => { - it('Should call update service on save for existing entity', fakeAsync(() => { - // GIVEN - const entity = new Share(123); - spyOn(service, 'update').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.share = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.update).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - - it('Should call create service on save for new entity', fakeAsync(() => { - // GIVEN - const entity = new Share(); - spyOn(service, 'create').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.share = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.create).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/share/share.component.spec.ts b/src/test/javascript/spec/app/entities/share/share.component.spec.ts deleted file mode 100644 index ab0e60bb..00000000 --- a/src/test/javascript/spec/app/entities/share/share.component.spec.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute, Data } from '@angular/router'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { ShareComponent } from 'app/entities/share/share.component'; -import { ShareService } from 'app/entities/share/share.service'; -import { Share } from 'app/shared/model/share.model'; - -describe('Component Tests', () => { - describe('Share Management Component', () => { - let comp: ShareComponent; - let fixture: ComponentFixture; - let service: ShareService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [ShareComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: { - data: { - subscribe: (fn: (value: Data) => void) => - fn({ - pagingParams: { - predicate: 'id', - reverse: false, - page: 0 - } - }) - } - } - } - ] - }) - .overrideTemplate(ShareComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(ShareComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(ShareService); - }); - - it('Should call load all on init', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Share(123)], - headers - }) - ) - ); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.shares[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should load a page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Share(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.shares[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should re-initialize the page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new Share(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - comp.reset(); - - // THEN - expect(comp.page).toEqual(0); - expect(service.query).toHaveBeenCalledTimes(2); - expect(comp.shares[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - it('should calculate the sort attribute for an id', () => { - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['id,asc']); - }); - - it('should calculate the sort attribute for a non-id attribute', () => { - // GIVEN - comp.predicate = 'name'; - - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['name,asc', 'id']); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/share/share.service.spec.ts b/src/test/javascript/spec/app/entities/share/share.service.spec.ts deleted file mode 100644 index 73bf3281..00000000 --- a/src/test/javascript/spec/app/entities/share/share.service.spec.ts +++ /dev/null @@ -1,142 +0,0 @@ -/* tslint:disable max-line-length */ -import { TestBed, getTestBed } from '@angular/core/testing'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { of } from 'rxjs'; -import { take, map } from 'rxjs/operators'; -import * as moment from 'moment'; -import { DATE_FORMAT } from 'app/shared/constants/input.constants'; -import { ShareService } from 'app/entities/share/share.service'; -import { IShare, Share, ShareAction } from 'app/shared/model/share.model'; - -describe('Service Tests', () => { - describe('Share Service', () => { - let injector: TestBed; - let service: ShareService; - let httpMock: HttpTestingController; - let elemDefault: IShare; - let currentDate: moment.Moment; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - injector = getTestBed(); - service = injector.get(ShareService); - httpMock = injector.get(HttpTestingController); - currentDate = moment(); - - elemDefault = new Share(0, currentDate, currentDate, ShareAction.SUBSCRIPTION, 0, 'AAAAAAA'); - }); - - describe('Service methods', async () => { - it('should find an element', async () => { - const returnedFromService = Object.assign( - { - documentDate: currentDate.format(DATE_FORMAT), - valueDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - service - .find(123) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: elemDefault })); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should create a Share', async () => { - const returnedFromService = Object.assign( - { - id: 0, - documentDate: currentDate.format(DATE_FORMAT), - valueDate: currentDate.format(DATE_FORMAT) - }, - elemDefault - ); - const expected = Object.assign( - { - documentDate: currentDate, - valueDate: currentDate - }, - returnedFromService - ); - service - .create(new Share(null)) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'POST' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should update a Share', async () => { - const returnedFromService = Object.assign( - { - documentDate: currentDate.format(DATE_FORMAT), - valueDate: currentDate.format(DATE_FORMAT), - action: 'BBBBBB', - quantity: 1, - remark: 'BBBBBB' - }, - elemDefault - ); - - const expected = Object.assign( - { - documentDate: currentDate, - valueDate: currentDate - }, - returnedFromService - ); - service - .update(expected) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'PUT' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should return a list of Share', async () => { - const returnedFromService = Object.assign( - { - documentDate: currentDate.format(DATE_FORMAT), - valueDate: currentDate.format(DATE_FORMAT), - action: 'BBBBBB', - quantity: 1, - remark: 'BBBBBB' - }, - elemDefault - ); - const expected = Object.assign( - { - documentDate: currentDate, - valueDate: currentDate - }, - returnedFromService - ); - service - .query(expected) - .pipe( - take(1), - map(resp => resp.body) - ) - .subscribe(body => expect(body).toContainEqual(expected)); - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify([returnedFromService])); - httpMock.verify(); - }); - - it('should delete a Share', async () => { - const rxPromise = service.delete(123).subscribe(resp => expect(resp.ok)); - - const req = httpMock.expectOne({ method: 'DELETE' }); - req.flush({ status: 200 }); - }); - }); - - afterEach(() => { - httpMock.verify(); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.spec.ts b/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.spec.ts deleted file mode 100644 index 359e218c..00000000 --- a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-delete-dialog.component.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Observable, of } from 'rxjs'; -import { JhiEventManager } from 'ng-jhipster'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { UserRoleAssignmentDeleteDialogComponent } from 'app/entities/user-role-assignment/user-role-assignment-delete-dialog.component'; -import { UserRoleAssignmentService } from 'app/entities/user-role-assignment/user-role-assignment.service'; - -describe('Component Tests', () => { - describe('UserRoleAssignment Management Delete Component', () => { - let comp: UserRoleAssignmentDeleteDialogComponent; - let fixture: ComponentFixture; - let service: UserRoleAssignmentService; - let mockEventManager: any; - let mockActiveModal: any; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [UserRoleAssignmentDeleteDialogComponent] - }) - .overrideTemplate(UserRoleAssignmentDeleteDialogComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(UserRoleAssignmentDeleteDialogComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(UserRoleAssignmentService); - mockEventManager = fixture.debugElement.injector.get(JhiEventManager); - mockActiveModal = fixture.debugElement.injector.get(NgbActiveModal); - }); - - describe('confirmDelete', () => { - it('Should call delete service on confirmDelete', inject( - [], - fakeAsync(() => { - // GIVEN - spyOn(service, 'delete').and.returnValue(of({})); - - // WHEN - comp.confirmDelete(123); - tick(); - - // THEN - expect(service.delete).toHaveBeenCalledWith(123); - expect(mockActiveModal.dismissSpy).toHaveBeenCalled(); - expect(mockEventManager.broadcastSpy).toHaveBeenCalled(); - }) - )); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-detail.component.spec.ts b/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-detail.component.spec.ts deleted file mode 100644 index d2f85209..00000000 --- a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-detail.component.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { UserRoleAssignmentDetailComponent } from 'app/entities/user-role-assignment/user-role-assignment-detail.component'; -import { UserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; - -describe('Component Tests', () => { - describe('UserRoleAssignment Management Detail Component', () => { - let comp: UserRoleAssignmentDetailComponent; - let fixture: ComponentFixture; - const route = ({ data: of({ userRoleAssignment: new UserRoleAssignment(123) }) } as any) as ActivatedRoute; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [UserRoleAssignmentDetailComponent], - providers: [{ provide: ActivatedRoute, useValue: route }] - }) - .overrideTemplate(UserRoleAssignmentDetailComponent, '') - .compileComponents(); - fixture = TestBed.createComponent(UserRoleAssignmentDetailComponent); - comp = fixture.componentInstance; - }); - - describe('OnInit', () => { - it('Should call load all on init', () => { - // GIVEN - - // WHEN - comp.ngOnInit(); - - // THEN - expect(comp.userRoleAssignment).toEqual(jasmine.objectContaining({ id: 123 })); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-update.component.spec.ts b/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-update.component.spec.ts deleted file mode 100644 index 0ca89214..00000000 --- a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment-update.component.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { HttpResponse } from '@angular/common/http'; -import { Observable, of } from 'rxjs'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { UserRoleAssignmentUpdateComponent } from 'app/entities/user-role-assignment/user-role-assignment-update.component'; -import { UserRoleAssignmentService } from 'app/entities/user-role-assignment/user-role-assignment.service'; -import { UserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; - -describe('Component Tests', () => { - describe('UserRoleAssignment Management Update Component', () => { - let comp: UserRoleAssignmentUpdateComponent; - let fixture: ComponentFixture; - let service: UserRoleAssignmentService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [UserRoleAssignmentUpdateComponent] - }) - .overrideTemplate(UserRoleAssignmentUpdateComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(UserRoleAssignmentUpdateComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(UserRoleAssignmentService); - }); - - describe('save', () => { - it('Should call update service on save for existing entity', fakeAsync(() => { - // GIVEN - const entity = new UserRoleAssignment(123); - spyOn(service, 'update').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.userRoleAssignment = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.update).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - - it('Should call create service on save for new entity', fakeAsync(() => { - // GIVEN - const entity = new UserRoleAssignment(); - spyOn(service, 'create').and.returnValue(of(new HttpResponse({ body: entity }))); - comp.userRoleAssignment = entity; - // WHEN - comp.save(); - tick(); // simulate async - - // THEN - expect(service.create).toHaveBeenCalledWith(entity); - expect(comp.isSaving).toEqual(false); - })); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment.component.spec.ts b/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment.component.spec.ts deleted file mode 100644 index 82e6923c..00000000 --- a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment.component.spec.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* tslint:disable max-line-length */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable, of } from 'rxjs'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute, Data } from '@angular/router'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { UserRoleAssignmentComponent } from 'app/entities/user-role-assignment/user-role-assignment.component'; -import { UserRoleAssignmentService } from 'app/entities/user-role-assignment/user-role-assignment.service'; -import { UserRoleAssignment } from 'app/shared/model/user-role-assignment.model'; - -describe('Component Tests', () => { - describe('UserRoleAssignment Management Component', () => { - let comp: UserRoleAssignmentComponent; - let fixture: ComponentFixture; - let service: UserRoleAssignmentService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [UserRoleAssignmentComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: { - data: { - subscribe: (fn: (value: Data) => void) => - fn({ - pagingParams: { - predicate: 'id', - reverse: false, - page: 0 - } - }) - } - } - } - ] - }) - .overrideTemplate(UserRoleAssignmentComponent, '') - .compileComponents(); - - fixture = TestBed.createComponent(UserRoleAssignmentComponent); - comp = fixture.componentInstance; - service = fixture.debugElement.injector.get(UserRoleAssignmentService); - }); - - it('Should call load all on init', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new UserRoleAssignment(123)], - headers - }) - ) - ); - - // WHEN - comp.ngOnInit(); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.userRoleAssignments[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should load a page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new UserRoleAssignment(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - - // THEN - expect(service.query).toHaveBeenCalled(); - expect(comp.userRoleAssignments[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - - it('should re-initialize the page', () => { - // GIVEN - const headers = new HttpHeaders().append('link', 'link;link'); - spyOn(service, 'query').and.returnValue( - of( - new HttpResponse({ - body: [new UserRoleAssignment(123)], - headers - }) - ) - ); - - // WHEN - comp.loadPage(1); - comp.reset(); - - // THEN - expect(comp.page).toEqual(0); - expect(service.query).toHaveBeenCalledTimes(2); - expect(comp.userRoleAssignments[0]).toEqual(jasmine.objectContaining({ id: 123 })); - }); - it('should calculate the sort attribute for an id', () => { - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['id,asc']); - }); - - it('should calculate the sort attribute for a non-id attribute', () => { - // GIVEN - comp.predicate = 'name'; - - // WHEN - const result = comp.sort(); - - // THEN - expect(result).toEqual(['name,asc', 'id']); - }); - }); -}); diff --git a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment.service.spec.ts b/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment.service.spec.ts deleted file mode 100644 index 5bcb7987..00000000 --- a/src/test/javascript/spec/app/entities/user-role-assignment/user-role-assignment.service.spec.ts +++ /dev/null @@ -1,108 +0,0 @@ -/* tslint:disable max-line-length */ -import { TestBed, getTestBed } from '@angular/core/testing'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { of } from 'rxjs'; -import { take, map } from 'rxjs/operators'; -import { UserRoleAssignmentService } from 'app/entities/user-role-assignment/user-role-assignment.service'; -import { IUserRoleAssignment, UserRoleAssignment, UserRole } from 'app/shared/model/user-role-assignment.model'; - -describe('Service Tests', () => { - describe('UserRoleAssignment Service', () => { - let injector: TestBed; - let service: UserRoleAssignmentService; - let httpMock: HttpTestingController; - let elemDefault: IUserRoleAssignment; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] - }); - injector = getTestBed(); - service = injector.get(UserRoleAssignmentService); - httpMock = injector.get(HttpTestingController); - - elemDefault = new UserRoleAssignment(0, 'AAAAAAA', 0, UserRole.HOSTMASTER); - }); - - describe('Service methods', async () => { - it('should find an element', async () => { - const returnedFromService = Object.assign({}, elemDefault); - service - .find(123) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: elemDefault })); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should create a UserRoleAssignment', async () => { - const returnedFromService = Object.assign( - { - id: 0 - }, - elemDefault - ); - const expected = Object.assign({}, returnedFromService); - service - .create(new UserRoleAssignment(null)) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'POST' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should update a UserRoleAssignment', async () => { - const returnedFromService = Object.assign( - { - entityTypeId: 'BBBBBB', - entityObjectId: 1, - assignedRole: 'BBBBBB' - }, - elemDefault - ); - - const expected = Object.assign({}, returnedFromService); - service - .update(expected) - .pipe(take(1)) - .subscribe(resp => expect(resp).toMatchObject({ body: expected })); - const req = httpMock.expectOne({ method: 'PUT' }); - req.flush(JSON.stringify(returnedFromService)); - }); - - it('should return a list of UserRoleAssignment', async () => { - const returnedFromService = Object.assign( - { - entityTypeId: 'BBBBBB', - entityObjectId: 1, - assignedRole: 'BBBBBB' - }, - elemDefault - ); - const expected = Object.assign({}, returnedFromService); - service - .query(expected) - .pipe( - take(1), - map(resp => resp.body) - ) - .subscribe(body => expect(body).toContainEqual(expected)); - const req = httpMock.expectOne({ method: 'GET' }); - req.flush(JSON.stringify([returnedFromService])); - httpMock.verify(); - }); - - it('should delete a UserRoleAssignment', async () => { - const rxPromise = service.delete(123).subscribe(resp => expect(resp.ok)); - - const req = httpMock.expectOne({ method: 'DELETE' }); - req.flush({ status: 200 }); - }); - }); - - afterEach(() => { - httpMock.verify(); - }); - }); -}); diff --git a/src/test/javascript/spec/app/shared/alert/alert-error.component.spec.ts b/src/test/javascript/spec/app/shared/alert/alert-error.component.spec.ts deleted file mode 100644 index d15e9622..00000000 --- a/src/test/javascript/spec/app/shared/alert/alert-error.component.spec.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { ComponentFixture, TestBed, async, inject, fakeAsync, tick } from '@angular/core/testing'; -import { HttpErrorResponse, HttpHeaders } from '@angular/common/http'; -import { JhiAlertService, JhiEventManager } from 'ng-jhipster'; -import { TranslateModule } from '@ngx-translate/core'; - -import { HsadminNgTestModule } from '../../../test.module'; -import { JhiAlertErrorComponent } from 'app/shared/alert/alert-error.component'; -import { MockAlertService } from '../../../helpers/mock-alert.service'; - -describe('Component Tests', () => { - describe('Alert Error Component', () => { - let comp: JhiAlertErrorComponent; - let fixture: ComponentFixture; - let eventManager: JhiEventManager; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule, TranslateModule.forRoot()], - declarations: [JhiAlertErrorComponent], - providers: [ - JhiEventManager, - { - provide: JhiAlertService, - useClass: MockAlertService - } - ] - }) - .overrideTemplate(JhiAlertErrorComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(JhiAlertErrorComponent); - comp = fixture.componentInstance; - eventManager = fixture.debugElement.injector.get(JhiEventManager); - }); - - describe('Error Handling', () => { - it('Should display an alert on status 0', () => { - // GIVEN - eventManager.broadcast({ name: 'hsadminNgApp.httpError', content: { status: 0 } }); - // THEN - expect(comp.alerts.length).toBe(1); - expect(comp.alerts[0].msg).toBe('error.server.not.reachable'); - }); - it('Should display an alert on status 404', () => { - // GIVEN - eventManager.broadcast({ name: 'hsadminNgApp.httpError', content: { status: 404 } }); - // THEN - expect(comp.alerts.length).toBe(1); - expect(comp.alerts[0].msg).toBe('error.url.not.found'); - }); - it('Should display an alert on generic error', () => { - // GIVEN - eventManager.broadcast({ name: 'hsadminNgApp.httpError', content: { error: { message: 'Error Message' } } }); - eventManager.broadcast({ name: 'hsadminNgApp.httpError', content: { error: 'Second Error Message' } }); - // THEN - expect(comp.alerts.length).toBe(2); - expect(comp.alerts[0].msg).toBe('Error Message'); - expect(comp.alerts[1].msg).toBe('Second Error Message'); - }); - it('Should display an alert on status 400 for generic error', () => { - // GIVEN - const response = new HttpErrorResponse({ - url: 'http://localhost:8080/api/foos', - headers: new HttpHeaders(), - status: 400, - statusText: 'Bad Request', - error: { - type: 'https://www.jhipster.tech/problem/constraint-violation', - title: 'Bad Request', - status: 400, - path: '/api/foos', - message: 'error.validation' - } - }); - eventManager.broadcast({ name: 'hsadminNgApp.httpError', content: response }); - // THEN - expect(comp.alerts.length).toBe(1); - expect(comp.alerts[0].msg).toBe('error.validation'); - }); - it('Should display an alert on status 400 for generic error without message', () => { - // GIVEN - const response = new HttpErrorResponse({ - url: 'http://localhost:8080/api/foos', - headers: new HttpHeaders(), - status: 400, - error: 'Bad Request' - }); - eventManager.broadcast({ name: 'hsadminNgApp.httpError', content: response }); - // THEN - expect(comp.alerts.length).toBe(1); - expect(comp.alerts[0].msg).toBe('Bad Request'); - }); - it('Should display an alert on status 400 for invalid parameters', () => { - // GIVEN - const response = new HttpErrorResponse({ - url: 'http://localhost:8080/api/foos', - headers: new HttpHeaders(), - status: 400, - statusText: 'Bad Request', - error: { - type: 'https://www.jhipster.tech/problem/constraint-violation', - title: 'Method argument not valid', - status: 400, - path: '/api/foos', - message: 'error.validation', - fieldErrors: [{ objectName: 'foo', field: 'minField', message: 'Min' }] - } - }); - eventManager.broadcast({ name: 'hsadminNgApp.httpError', content: response }); - // THEN - expect(comp.alerts.length).toBe(1); - expect(comp.alerts[0].msg).toBe('error.Size'); - }); - it('Should display an alert on status 400 for error headers', () => { - // GIVEN - const response = new HttpErrorResponse({ - url: 'http://localhost:8080/api/foos', - headers: new HttpHeaders().append('app-error', 'Error Message').append('app-params', 'foo'), - status: 400, - statusText: 'Bad Request', - error: { - status: 400, - message: 'error.validation' - } - }); - eventManager.broadcast({ name: 'hsadminNgApp.httpError', content: response }); - // THEN - expect(comp.alerts.length).toBe(1); - expect(comp.alerts[0].msg).toBe('Error Message'); - }); - }); - }); -}); diff --git a/src/test/javascript/spec/app/shared/login/login.component.spec.ts b/src/test/javascript/spec/app/shared/login/login.component.spec.ts deleted file mode 100644 index f1bcfb71..00000000 --- a/src/test/javascript/spec/app/shared/login/login.component.spec.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { ComponentFixture, TestBed, async, inject, fakeAsync, tick } from '@angular/core/testing'; -import { Router } from '@angular/router'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { JhiEventManager } from 'ng-jhipster'; - -import { LoginService } from 'app/core/login/login.service'; -import { JhiLoginModalComponent } from 'app/shared/login/login.component'; -import { StateStorageService } from 'app/core/auth/state-storage.service'; -import { HsadminNgTestModule } from '../../../test.module'; -import { MockLoginService } from '../../../helpers/mock-login.service'; -import { MockStateStorageService } from '../../../helpers/mock-state-storage.service'; - -describe('Component Tests', () => { - describe('LoginComponent', () => { - let comp: JhiLoginModalComponent; - let fixture: ComponentFixture; - let mockLoginService: any; - let mockStateStorageService: any; - let mockRouter: any; - let mockEventManager: any; - let mockActiveModal: any; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HsadminNgTestModule], - declarations: [JhiLoginModalComponent], - providers: [ - { - provide: LoginService, - useClass: MockLoginService - }, - { - provide: StateStorageService, - useClass: MockStateStorageService - } - ] - }) - .overrideTemplate(JhiLoginModalComponent, '') - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(JhiLoginModalComponent); - comp = fixture.componentInstance; - mockLoginService = fixture.debugElement.injector.get(LoginService); - mockStateStorageService = fixture.debugElement.injector.get(StateStorageService); - mockRouter = fixture.debugElement.injector.get(Router); - mockEventManager = fixture.debugElement.injector.get(JhiEventManager); - mockActiveModal = fixture.debugElement.injector.get(NgbActiveModal); - }); - - it('should authenticate the user upon login when previous state was set', inject( - [], - fakeAsync(() => { - // GIVEN - const credentials = { - username: 'admin', - password: 'admin', - rememberMe: true - }; - comp.username = 'admin'; - comp.password = 'admin'; - comp.rememberMe = true; - comp.credentials = credentials; - mockLoginService.setResponse({}); - mockStateStorageService.setResponse({ redirect: 'dummy' }); - - // WHEN/ - comp.login(); - tick(); // simulate async - - // THEN - expect(comp.authenticationError).toEqual(false); - expect(mockActiveModal.dismissSpy).toHaveBeenCalledWith('login success'); - expect(mockEventManager.broadcastSpy).toHaveBeenCalledTimes(1); - expect(mockLoginService.loginSpy).toHaveBeenCalledWith(credentials); - expect(mockStateStorageService.getUrlSpy).toHaveBeenCalledTimes(1); - expect(mockStateStorageService.storeUrlSpy).toHaveBeenCalledWith(null); - expect(mockRouter.navigateSpy).toHaveBeenCalledWith([{ redirect: 'dummy' }]); - }) - )); - - it('should authenticate the user upon login when previous state was not set', inject( - [], - fakeAsync(() => { - // GIVEN - const credentials = { - username: 'admin', - password: 'admin', - rememberMe: true - }; - comp.username = 'admin'; - comp.password = 'admin'; - comp.rememberMe = true; - comp.credentials = credentials; - mockLoginService.setResponse({}); - mockStateStorageService.setResponse(null); - - // WHEN - comp.login(); - tick(); // simulate async - - // THEN - expect(comp.authenticationError).toEqual(false); - expect(mockActiveModal.dismissSpy).toHaveBeenCalledWith('login success'); - expect(mockEventManager.broadcastSpy).toHaveBeenCalledTimes(1); - expect(mockLoginService.loginSpy).toHaveBeenCalledWith(credentials); - expect(mockStateStorageService.getUrlSpy).toHaveBeenCalledTimes(1); - expect(mockStateStorageService.storeUrlSpy).not.toHaveBeenCalled(); - expect(mockRouter.navigateSpy).not.toHaveBeenCalled(); - }) - )); - - it('should empty the credentials upon cancel', () => { - // GIVEN - const credentials = { - username: 'admin', - password: 'admin', - rememberMe: true - }; - - const expected = { - username: null, - password: null, - rememberMe: true - }; - - comp.credentials = credentials; - - // WHEN - comp.cancel(); - - // THEN - expect(comp.authenticationError).toEqual(false); - expect(comp.credentials).toEqual(expected); - expect(mockActiveModal.dismissSpy).toHaveBeenCalledWith('cancel'); - }); - - it('should redirect user when register', () => { - // WHEN - comp.register(); - - // THEN - expect(mockActiveModal.dismissSpy).toHaveBeenCalledWith('to state register'); - expect(mockRouter.navigateSpy).toHaveBeenCalledWith(['/register']); - }); - - it('should redirect user when request password', () => { - // WHEN - comp.requestResetPassword(); - - // THEN - expect(mockActiveModal.dismissSpy).toHaveBeenCalledWith('to state requestReset'); - expect(mockRouter.navigateSpy).toHaveBeenCalledWith(['/reset', 'request']); - }); - }); -}); diff --git a/src/test/javascript/spec/app/shared/util/linebreaks-pipe.spec.ts b/src/test/javascript/spec/app/shared/util/linebreaks-pipe.spec.ts deleted file mode 100644 index 28e20578..00000000 --- a/src/test/javascript/spec/app/shared/util/linebreaks-pipe.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { LinebreaksPipe } from 'app/shared/util/linebreaks-pipe'; - -/* To run these tests in IntelliJ IDEA, you need a run configuration with - Configuration File: - ~/Projekte/Hostsharing/hsadmin-ng/src/test/javascript/jest.conf.js - and a Node Interpreter, e.g. if installed with nvm, this could be: - ~/.nvm/versions/node/v10.15.3/bin/node - */ -describe('LinebreaksPipe Tests', () => { - describe('LinebreaksPipe', () => { - let pipe: LinebreaksPipe; - - beforeEach(() => { - pipe = new LinebreaksPipe(); - }); - - it('converts null to null', () => { - expect(pipe.transform(null)).toBe(null); - }); - - it('converts empty string to empty string', () => { - expect(pipe.transform('')).toBe(''); - }); - - it('converts string not containing line breaks to identical string', () => { - expect(pipe.transform('no linebreak here')).toBe('no linebreak here'); - }); - - it('converts string containing line breaks to string containing
by default', () => { - expect(pipe.transform('some\nlinebreaks\nhere')).toBe('some
linebreaks
here'); - }); - - it('converts string containing line breaks string containing specified replacement', () => { - expect(pipe.transform('some\nlinebreaks\nhere', ' | ')).toBe('some | linebreaks | here'); - }); - }); -}); diff --git a/src/test/javascript/spec/app/shared/util/tablefilter.spec.ts b/src/test/javascript/spec/app/shared/util/tablefilter.spec.ts deleted file mode 100644 index 8f523776..00000000 --- a/src/test/javascript/spec/app/shared/util/tablefilter.spec.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { queryContains, queryEquals, queryYearAsDateRange, TableFilter } from 'app/shared/util/tablefilter'; - -/* To run these tests in IntelliJ IDEA, you need a run configuration with - Configuration File: - ~/Projekte/Hostsharing/hsadmin-ng/src/test/javascript/jest.conf.js - and a Node Interpreter, e.g. if installed with nvm, this could be: - ~/.nvm/versions/node/v10.15.3/bin/node - */ -describe('TableFilter Tests', () => { - describe('TableFilter', () => { - const TEST_DEBOUNCE_MILLIS = 100; - - let filter: TableFilter<{ name?: string; number?: string; date?: string }>; - let asynchronously: () => void; - - beforeEach(() => { - filter = new TableFilter( - { - name: queryContains, - number: queryEquals, - date: queryYearAsDateRange - }, - TEST_DEBOUNCE_MILLIS, - () => { - asynchronously(); - } - ); - }); - - it('trigger() asynchronously calls the reload-handler', done => { - // given - filter.criteria.name = 'Test Filter Value'; - - // when - filter.trigger({ target: { valid: true } }); - const triggerStartedAtMillis = Date.now(); - - // then - asynchronously = () => { - expect(Date.now()).toBeGreaterThan(triggerStartedAtMillis); - done(); - }; - }); - - it('if trigger() is called multiple times during debounce, debounce period ands after last trigger()', done => { - // given - filter.criteria.name = 'Test Filter Value'; - - // when - filter.trigger({ target: { valid: true } }); - let triggerStartedAtMillis = null; - setTimeout(() => { - filter.trigger({ target: { valid: true } }); - triggerStartedAtMillis = Date.now(); - }, 50); - - // then - asynchronously = () => { - expect(triggerStartedAtMillis).not.toBeNull(); - expect(Date.now()).toBeGreaterThan(triggerStartedAtMillis); - done(); - }; - }); - - it('when filter "name" is set, buildQueryCriteria() returns a name.contains query', () => { - // given - filter.criteria.name = 'test value'; - - // when - const actual = filter.buildQueryCriteria(); - - // then - expect(filter.buildQueryCriteria()).toEqual({ 'name.contains': 'test value' }); - }); - - it('when filter "name" is set to "--", buildQueryCriteria() returns a name.specified=false query', () => { - // given - filter.criteria.name = '--'; - - // when - const actual = filter.buildQueryCriteria(); - - // then - expect(filter.buildQueryCriteria()).toEqual({ 'name.specified': false }); - }); - - it('when filter "name" is set to "++", buildQueryCriteria() returns a name.specified=true query', () => { - // given - filter.criteria.name = '++'; - - // when - const actual = filter.buildQueryCriteria(); - - // then - expect(filter.buildQueryCriteria()).toEqual({ 'name.specified': true }); - }); - - it('when filter "number" is set, buildQueryCriteria() returns a number.equals query', () => { - // given - filter.criteria.number = '-42'; - - // when - const actual = filter.buildQueryCriteria(); - - // then - expect(filter.buildQueryCriteria()).toEqual({ 'number.equals': '-42' }); - }); - - it('when filter "date" is set to "2019", buildQueryCriteria() returns a date range query', () => { - // given - filter.criteria.date = '2019'; - - // when - const actual = filter.buildQueryCriteria(); - - // then - expect(filter.buildQueryCriteria()).toEqual({ 'date.greaterOrEqualThan': '2019-01-01', 'date.lessOrEqualThan': '2019-12-31' }); - }); - - it('queryYearAsDateRange() returns null if year is not 4-digit', () => { - expect(queryYearAsDateRange('date', '201')).toBeNull(); - expect(queryYearAsDateRange('date', '20191')).toBeNull(); - }); - - it('reset() clears criteria and calls reload-handler, considering debounce period', done => { - // given - filter.criteria.name = 'Test Filter Value'; - - // when - filter.trigger({ target: { valid: true } }); - let triggerStartedAtMillis = null; - setTimeout(() => { - filter.reset(); - triggerStartedAtMillis = Date.now(); - }, TEST_DEBOUNCE_MILLIS / 2); - - // then - asynchronously = () => { - expect(triggerStartedAtMillis).not.toBeNull(); - expect(Date.now()).toBeGreaterThan(triggerStartedAtMillis); - done(); - }; - }); - }); -}); diff --git a/src/test/javascript/spec/helpers/mock-account.service.ts b/src/test/javascript/spec/helpers/mock-account.service.ts deleted file mode 100644 index 659bf4d3..00000000 --- a/src/test/javascript/spec/helpers/mock-account.service.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { SpyObject } from './spyobject'; -import { AccountService } from 'app/core/auth/account.service'; -import Spy = jasmine.Spy; - -export class MockAccountService extends SpyObject { - getSpy: Spy; - saveSpy: Spy; - fakeResponse: any; - identitySpy: Spy; - - constructor() { - super(AccountService); - - this.fakeResponse = null; - this.getSpy = this.spy('get').andReturn(this); - this.saveSpy = this.spy('save').andReturn(this); - this.setIdentitySpy({}); - } - - subscribe(callback: any) { - callback(this.fakeResponse); - } - - setResponse(json: any): void { - this.fakeResponse = json; - } - - setIdentitySpy(json: any): any { - this.identitySpy = this.spy('identity').andReturn(Promise.resolve(json)); - } - - setIdentityResponse(json: any): void { - this.setIdentitySpy(json); - } -} diff --git a/src/test/javascript/spec/helpers/mock-active-modal.service.ts b/src/test/javascript/spec/helpers/mock-active-modal.service.ts deleted file mode 100644 index 8bf0cc96..00000000 --- a/src/test/javascript/spec/helpers/mock-active-modal.service.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { SpyObject } from './spyobject'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import Spy = jasmine.Spy; - -export class MockActiveModal extends SpyObject { - dismissSpy: Spy; - - constructor() { - super(NgbActiveModal); - this.dismissSpy = this.spy('dismiss').andReturn(this); - } -} diff --git a/src/test/javascript/spec/helpers/mock-alert.service.ts b/src/test/javascript/spec/helpers/mock-alert.service.ts deleted file mode 100644 index 87f36c71..00000000 --- a/src/test/javascript/spec/helpers/mock-alert.service.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { SpyObject } from './spyobject'; -import { JhiAlertService, JhiAlert } from 'ng-jhipster'; - -export class MockAlertService extends SpyObject { - constructor() { - super(JhiAlertService); - } - addAlert(alertOptions: JhiAlert) { - return alertOptions; - } -} diff --git a/src/test/javascript/spec/helpers/mock-event-manager.service.ts b/src/test/javascript/spec/helpers/mock-event-manager.service.ts deleted file mode 100644 index a71b5d93..00000000 --- a/src/test/javascript/spec/helpers/mock-event-manager.service.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { SpyObject } from './spyobject'; -import { JhiEventManager } from 'ng-jhipster'; -import Spy = jasmine.Spy; - -export class MockEventManager extends SpyObject { - broadcastSpy: Spy; - - constructor() { - super(JhiEventManager); - this.broadcastSpy = this.spy('broadcast').andReturn(this); - } -} diff --git a/src/test/javascript/spec/helpers/mock-language.service.ts b/src/test/javascript/spec/helpers/mock-language.service.ts deleted file mode 100644 index e10db075..00000000 --- a/src/test/javascript/spec/helpers/mock-language.service.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { SpyObject } from './spyobject'; -import { JhiLanguageService } from 'ng-jhipster'; -import { JhiLanguageHelper } from 'app/core/language/language.helper'; -import Spy = jasmine.Spy; - -export class MockLanguageService extends SpyObject { - getCurrentSpy: Spy; - fakeResponse: any; - - constructor() { - super(JhiLanguageService); - - this.fakeResponse = 'de'; - this.getCurrentSpy = this.spy('getCurrent').andReturn(Promise.resolve(this.fakeResponse)); - } - - init() {} - - changeLanguage(languageKey: string) {} - - setLocations(locations: string[]) {} - - addLocation(location: string) {} - - reload() {} -} - -export class MockLanguageHelper extends SpyObject { - getAllSpy: Spy; - - constructor() { - super(JhiLanguageHelper); - - this.getAllSpy = this.spy('getAll').andReturn(Promise.resolve(['en', 'fr'])); - } -} diff --git a/src/test/javascript/spec/helpers/mock-login.service.ts b/src/test/javascript/spec/helpers/mock-login.service.ts deleted file mode 100644 index 93a8ca57..00000000 --- a/src/test/javascript/spec/helpers/mock-login.service.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { SpyObject } from './spyobject'; -import { LoginService } from 'app/core/login/login.service'; -import Spy = jasmine.Spy; - -export class MockLoginService extends SpyObject { - loginSpy: Spy; - logoutSpy: Spy; - registerSpy: Spy; - requestResetPasswordSpy: Spy; - cancelSpy: Spy; - - constructor() { - super(LoginService); - - this.setLoginSpy({}); - this.logoutSpy = this.spy('logout').andReturn(this); - this.registerSpy = this.spy('register').andReturn(this); - this.requestResetPasswordSpy = this.spy('requestResetPassword').andReturn(this); - this.cancelSpy = this.spy('cancel').andReturn(this); - } - - setLoginSpy(json: any) { - this.loginSpy = this.spy('login').andReturn(Promise.resolve(json)); - } - - setResponse(json: any): void { - this.setLoginSpy(json); - } -} diff --git a/src/test/javascript/spec/helpers/mock-route.service.ts b/src/test/javascript/spec/helpers/mock-route.service.ts deleted file mode 100644 index 3465e055..00000000 --- a/src/test/javascript/spec/helpers/mock-route.service.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ActivatedRoute, Router } from '@angular/router'; -import { SpyObject } from './spyobject'; -import { Observable, of } from 'rxjs'; -import Spy = jasmine.Spy; - -export class MockActivatedRoute extends ActivatedRoute { - constructor(parameters?: any) { - super(); - this.queryParams = of(parameters); - this.params = of(parameters); - this.data = of({ - ...parameters, - pagingParams: { - page: 10, - ascending: false, - predicate: 'id' - } - }); - } -} - -export class MockRouter extends SpyObject { - navigateSpy: Spy; - - constructor() { - super(Router); - this.navigateSpy = this.spy('navigate'); - } -} diff --git a/src/test/javascript/spec/helpers/mock-state-storage.service.ts b/src/test/javascript/spec/helpers/mock-state-storage.service.ts deleted file mode 100644 index 1398c7b2..00000000 --- a/src/test/javascript/spec/helpers/mock-state-storage.service.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SpyObject } from './spyobject'; -import { StateStorageService } from 'app/core/auth/state-storage.service'; -import Spy = jasmine.Spy; - -export class MockStateStorageService extends SpyObject { - getUrlSpy: Spy; - storeUrlSpy: Spy; - - constructor() { - super(StateStorageService); - this.setUrlSpy({}); - this.storeUrlSpy = this.spy('storeUrl').andReturn(this); - } - - setUrlSpy(json) { - this.getUrlSpy = this.spy('getUrl').andReturn(json); - } - - setResponse(json: any): void { - this.setUrlSpy(json); - } -} diff --git a/src/test/javascript/spec/helpers/spyobject.ts b/src/test/javascript/spec/helpers/spyobject.ts deleted file mode 100644 index 949e067e..00000000 --- a/src/test/javascript/spec/helpers/spyobject.ts +++ /dev/null @@ -1,69 +0,0 @@ -export interface GuinessCompatibleSpy extends jasmine.Spy { - /** By chaining the spy with and.returnValue, all calls to the function will return a specific - * value. */ - andReturn(val: any): void; - /** By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied - * function. */ - andCallFake(fn: Function): GuinessCompatibleSpy; - /** removes all recorded calls */ - reset(); -} - -export class SpyObject { - static stub(object = null, config = null, overrides = null) { - if (!(object instanceof SpyObject)) { - overrides = config; - config = object; - object = new SpyObject(); - } - - const m = {}; - Object.keys(config).forEach(key => (m[key] = config[key])); - Object.keys(overrides).forEach(key => (m[key] = overrides[key])); - Object.keys(m).forEach(key => { - object.spy(key).andReturn(m[key]); - }); - return object; - } - - constructor(type = null) { - if (type) { - Object.keys(type.prototype).forEach(prop => { - let m = null; - try { - m = type.prototype[prop]; - } catch (e) { - // As we are creating spys for abstract classes, - // these classes might have getters that throw when they are accessed. - // As we are only auto creating spys for methods, this - // should not matter. - } - if (typeof m === 'function') { - this.spy(prop); - } - }); - } - } - - spy(name) { - if (!this[name]) { - this[name] = this._createGuinnessCompatibleSpy(name); - } - return this[name]; - } - - prop(name, value) { - this[name] = value; - } - - /** @internal */ - _createGuinnessCompatibleSpy(name): GuinessCompatibleSpy { - const newSpy: GuinessCompatibleSpy = jasmine.createSpy(name); - newSpy.andCallFake = newSpy.and.callFake; - newSpy.andReturn = newSpy.and.returnValue; - newSpy.reset = newSpy.calls.reset; - // revisit return null here (previously needed for rtts_assert). - newSpy.and.returnValue(null); - return newSpy; - } -} diff --git a/src/test/javascript/spec/test.module.ts b/src/test/javascript/spec/test.module.ts deleted file mode 100644 index 1eb01806..00000000 --- a/src/test/javascript/spec/test.module.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { DatePipe } from '@angular/common'; -import { ActivatedRoute, Router } from '@angular/router'; -import { NgModule, ElementRef, Renderer } from '@angular/core'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { JhiLanguageService, JhiDataUtils, JhiDateUtils, JhiEventManager, JhiAlertService, JhiParseLinks } from 'ng-jhipster'; - -import { MockLanguageService, MockLanguageHelper } from './helpers/mock-language.service'; -import { JhiLanguageHelper, AccountService, LoginModalService } from 'app/core'; -import { MockAccountService } from './helpers/mock-account.service'; -import { MockActivatedRoute, MockRouter } from './helpers/mock-route.service'; -import { MockActiveModal } from './helpers/mock-active-modal.service'; -import { MockEventManager } from './helpers/mock-event-manager.service'; - -@NgModule({ - providers: [ - DatePipe, - JhiDataUtils, - JhiDateUtils, - JhiParseLinks, - { - provide: JhiLanguageService, - useClass: MockLanguageService - }, - { - provide: JhiLanguageHelper, - useClass: MockLanguageHelper - }, - { - provide: JhiEventManager, - useClass: MockEventManager - }, - { - provide: NgbActiveModal, - useClass: MockActiveModal - }, - { - provide: ActivatedRoute, - useValue: new MockActivatedRoute({ id: 123 }) - }, - { - provide: Router, - useClass: MockRouter - }, - { - provide: AccountService, - useClass: MockAccountService - }, - { - provide: LoginModalService, - useValue: null - }, - { - provide: ElementRef, - useValue: null - }, - { - provide: Renderer, - useValue: null - }, - { - provide: JhiAlertService, - useValue: null - }, - { - provide: NgbModal, - useValue: null - } - ], - imports: [HttpClientTestingModule] -}) -export class HsadminNgTestModule {} diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml deleted file mode 100644 index 47b64299..00000000 --- a/src/test/resources/config/application.yml +++ /dev/null @@ -1,107 +0,0 @@ -# =================================================================== -# Spring Boot configuration. -# -# This configuration is used for unit/integration tests. -# -# More information on profiles: https://www.jhipster.tech/profiles/ -# More information on configuration properties: https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -# =================================================================== -# Standard Spring Boot properties. -# Full reference is available at: -# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html -# =================================================================== - - -spring: - application: - name: hsadminNg - cache: - type: simple - datasource: - type: com.zaxxer.hikari.HikariDataSource - url: jdbc:h2:mem:hsadminNg;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE - name: - username: - password: - hikari: - auto-commit: false - jpa: - database-platform: io.github.jhipster.domain.util.FixedH2Dialect - database: H2 - open-in-view: false - show-sql: false - hibernate: - ddl-auto: none - naming: - physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy - implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy - properties: - hibernate.id.new_generator_mappings: true - hibernate.connection.provider_disables_autocommit: true - hibernate.cache.use_second_level_cache: false - hibernate.cache.use_query_cache: false - hibernate.generate_statistics: false - hibernate.hbm2ddl.auto: validate - hibernate.jdbc.time_zone: UTC - liquibase: - contexts: test - mail: - host: localhost - messages: - basename: i18n/messages - mvc: - favicon: - enabled: false - thymeleaf: - mode: HTML - - -server: - port: 10344 - address: localhost - -# =================================================================== -# JHipster specific properties -# -# Full reference is available at: https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -jhipster: - async: - core-pool-size: 1 - max-pool-size: 50 - queue-capacity: 10000 - # To test logstash appender - logging: - logstash: - enabled: true - host: localhost - port: 5000 - queue-size: 512 - mail: - from: test@localhost - base-url: http://127.0.0.1:8080 - security: - authentication: - jwt: - # This token must be encoded using Base64 (you can type `echo 'secret-key'|base64` on your command line) - base64-secret: ZDFlMDUzODIzMTUzZDEwZjExN2E5ZjAzY2VhZmYzNDE1YjhlYWUxZGRhMGU3ODZiNjRkNjVlNzEwZjExYWY4YzczM2NlYzI5YWE1OTRkNWM0YThlYjZjZjA5Zjc5YWJkOTgzYjdhZjQxZWQyZGUyYjFlYjI5ZDE3NmE4M2UzYjQ= - # Token is valid 24 hours - token-validity-in-seconds: 86400 - metrics: - logs: # Reports metrics in the logs - enabled: true - report-frequency: 60 # in seconds - -# =================================================================== -# Application specific properties -# Add your own application properties here, see the ApplicationProperties class -# to have type-safe configuration, like in the JHipsterProperties above -# -# More documentation is available at: -# https://www.jhipster.tech/common-application-properties/ -# =================================================================== - -# application: diff --git a/src/test/resources/i18n/messages_en.properties b/src/test/resources/i18n/messages_en.properties deleted file mode 100644 index f19db869..00000000 --- a/src/test/resources/i18n/messages_en.properties +++ /dev/null @@ -1 +0,0 @@ -email.test.title=test title diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml deleted file mode 100644 index ed3ff9d7..00000000 --- a/src/test/resources/logback.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/test/resources/templates/mail/testEmail.html b/src/test/resources/templates/mail/testEmail.html deleted file mode 100644 index a4ca16a7..00000000 --- a/src/test/resources/templates/mail/testEmail.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tsconfig-aot.json b/tsconfig-aot.json deleted file mode 100644 index be204eb4..00000000 --- a/tsconfig-aot.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "module": "es2015", - "moduleResolution": "node", - "sourceMap": false, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "removeComments": false, - "noImplicitAny": false, - "suppressImplicitAnyIndexErrors": true, - "skipLibCheck": true, - "outDir": "build/www/app", - "lib": ["es7", "dom"], - "typeRoots": ["node_modules/@types"], - "baseUrl": "./", - "paths": { - "app/*": ["src/main/webapp/app/*"] - }, - "importHelpers": true - }, - "angularCompilerOptions": { - "genDir": "build/aot", - "skipMetadataEmit": true, - "fullTemplateTypeCheck": true, - "preserveWhitespaces": true - } -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 7bc3ab32..00000000 --- a/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "moduleResolution": "node", - "sourceMap": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "removeComments": false, - "noImplicitAny": false, - "skipLibCheck": true, - "suppressImplicitAnyIndexErrors": true, - "outDir": "build/www/app", - "lib": ["es7", "dom"], - "typeRoots": ["node_modules/@types"], - "baseUrl": "./", - "paths": { - "app/*": ["src/main/webapp/app/*"] - }, - "importHelpers": true, - "allowJs": true - }, - "include": ["src/main/webapp/app", "src/test/javascript/"], - "exclude": ["node_modules"] -} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 85362d0f..00000000 --- a/tslint.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "rulesDirectory": ["node_modules/codelyzer"], - "extends": ["tslint-config-prettier"], - "rules": { - "class-name": true, - "comment-format": [true, "check-space"], - "curly": true, - "eofline": true, - "forin": true, - "indent": [true, "spaces"], - "label-position": true, - "member-access": false, - "member-ordering": [true, "static-before-instance", "variables-before-functions"], - "no-arg": true, - "no-bitwise": true, - "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-construct": true, - "no-debugger": true, - "no-duplicate-variable": true, - "no-empty": false, - "no-eval": true, - "no-inferrable-types": [true, "ignore-params", "ignore-properties"], - "no-shadowed-variable": true, - "no-string-literal": false, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, - "no-unused-expression": true, - "no-var-keyword": true, - "object-literal-sort-keys": false, - "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], - "quotemark": [true, "single", "avoid-escape"], - "radix": true, - "semicolon": [true, "always", "ignore-bound-class-methods"], - "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": false, - "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"], - "prefer-const": true, - "arrow-parens": [true, "ban-single-arg-parens"], - "arrow-return-shorthand": [true], - "import-spacing": true, - "no-consecutive-blank-lines": [true], - "object-literal-shorthand": true, - "space-before-function-paren": [ - true, - { - "asyncArrow": "always", - "anonymous": "never", - "constructor": "never", - "method": "never", - "named": "never" - } - ], - - "directive-selector": [true, "attribute", "jhi", "camelCase"], - "component-selector": [true, "element", "jhi", "kebab-case"], - "use-input-property-decorator": true, - "use-output-property-decorator": true, - "use-host-property-decorator": true, - "no-input-rename": true, - "no-output-rename": true, - "use-life-cycle-interface": true, - "use-pipe-transform-interface": false, - "component-class-suffix": true, - "directive-class-suffix": true - } -} diff --git a/vue/.gitignore b/vue/.gitignore deleted file mode 100644 index 185e6631..00000000 --- a/vue/.gitignore +++ /dev/null @@ -1,21 +0,0 @@ -.DS_Store -node_modules -/dist - -# local env files -.env.local -.env.*.local - -# Log files -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Editor directories and files -.idea -.vscode -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw* diff --git a/vue/README.md b/vue/README.md deleted file mode 100644 index c3767b9f..00000000 --- a/vue/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# hsadmin-vue - -## Project setup -``` -npm install -``` - -### Compiles and hot-reloads for development -``` -npm run serve -``` - -Change the port Vue binds to via the `--port` parameter. -Use the `VUE_APP_API_PORT` environment variable to point Vue to the JHipster API port. -For example, to specify both settings: - -``` -VUE_APP_API_PORT=45678 npm run serve -- --port=2345 -``` - -### Compiles and minifies for production -``` -npm run build -``` - -### Run your tests -``` -npm run test -``` - -### Lints and fixes files -``` -npm run lint -``` - -### Customize configuration -See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/vue/babel.config.js b/vue/babel.config.js deleted file mode 100644 index fd1df2a3..00000000 --- a/vue/babel.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - presets: [ - '@vue/app' - ] -}; diff --git a/vue/package-lock.json b/vue/package-lock.json deleted file mode 100644 index 52804c1e..00000000 --- a/vue/package-lock.json +++ /dev/null @@ -1,11184 +0,0 @@ -{ - "name": "hsadmin-vue", - "version": "0.1.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/core": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.3.tgz", - "integrity": "sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.0", - "@babel/helpers": "^7.4.3", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.11", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - } - }, - "@babel/generator": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz", - "integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", - "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", - "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-call-delegate": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.0.tgz", - "integrity": "sha512-SdqDfbVdNQCBp3WhK2mNdDvHd3BD6qbmIc43CAyjnsfCmgHMeqgDcM3BzY2lchi7HBJGJ2CVdynLWbezaE4mmQ==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.4.0", - "@babel/traverse": "^7.4.0", - "@babel/types": "^7.4.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.4.3.tgz", - "integrity": "sha512-UMl3TSpX11PuODYdWGrUeW6zFkdYhDn7wRLrOuNVM6f9L+S9CzmDXYyrp3MTHcwWjnzur1f/Op8A7iYZWya2Yg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-member-expression-to-functions": "^7.0.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.4.0", - "@babel/helper-split-export-declaration": "^7.4.0" - } - }, - "@babel/helper-define-map": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.4.0.tgz", - "integrity": "sha512-wAhQ9HdnLIywERVcSvX40CEJwKdAa1ID4neI9NXQPDOHwwA+57DqwLiPEVy2AIyWzAk0CQ8qx4awO0VUURwLtA==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/types": "^7.4.0", - "lodash": "^4.17.11" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", - "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.0.tgz", - "integrity": "sha512-/NErCuoe/et17IlAQFKWM24qtyYYie7sFIrW/tIQXpck6vAu2hhtYYsKLBWQV+BQZMbcIYPU/QMYuTufrY4aQw==", - "dev": true, - "requires": { - "@babel/types": "^7.4.0" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz", - "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", - "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-module-transforms": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.3.tgz", - "integrity": "sha512-H88T9IySZW25anu5uqyaC1DaQre7ofM+joZtAaO2F8NBdFfupH0SZ4gKjgSFVcvtx/aAirqA9L9Clio2heYbZA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/template": "^7.2.2", - "@babel/types": "^7.2.2", - "lodash": "^4.17.11" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", - "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.3.tgz", - "integrity": "sha512-hnoq5u96pLCfgjXuj8ZLX3QQ+6nAulS+zSgi6HulUwFbEruRAKwbGLU5OvXkE14L8XW6XsQEKsIDfgthKLRAyA==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", - "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-wrap-function": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-replace-supers": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.0.tgz", - "integrity": "sha512-PVwCVnWWAgnal+kJ+ZSAphzyl58XrFeSKSAJRiqg5QToTsjL+Xu1f9+RJ+d+Q0aPhPfBGaYfkox66k86thxNSg==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.0.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/traverse": "^7.4.0", - "@babel/types": "^7.4.0" - } - }, - "@babel/helper-simple-access": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", - "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", - "dev": true, - "requires": { - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz", - "integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==", - "dev": true, - "requires": { - "@babel/types": "^7.4.0" - } - }, - "@babel/helper-wrap-function": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", - "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.2.0" - } - }, - "@babel/helpers": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.3.tgz", - "integrity": "sha512-BMh7X0oZqb36CfyhvtbSmcWc3GXocfxv3yNsAEuM0l+fAqSO22rQrUpijr3oE/10jCTrB6/0b9kzmG4VetCj8Q==", - "dev": true, - "requires": { - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.3.tgz", - "integrity": "sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", - "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0", - "@babel/plugin-syntax-async-generators": "^7.2.0" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.0.tgz", - "integrity": "sha512-t2ECPNOXsIeK1JxJNKmgbzQtoG27KIlVE61vTqX0DKR9E9sZlVVxWUtEW9D5FlZ8b8j7SBNCHY47GgPKCKlpPg==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.4.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-proposal-decorators": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.0.tgz", - "integrity": "sha512-d08TLmXeK/XbgCo7ZeZ+JaeZDtDai/2ctapTRsWWkkmy7G/cqz8DQN/HlWG7RR4YmfXxmExsbU3SuCjlM7AtUg==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.4.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-decorators": "^7.2.0" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", - "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.2.0" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.3.tgz", - "integrity": "sha512-xC//6DNSSHVjq8O2ge0dyYlhshsH4T7XdCVoxbi5HzLYWfsC5ooFlJjrXk8RcAT+hjHAK9UjBXdylzSoDK3t4g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.0.tgz", - "integrity": "sha512-h/KjEZ3nK9wv1P1FSNb9G079jXrNYR0Ko+7XkOx85+gM24iZbPn0rh4vCftk+5QKY7y1uByFataBTmX7irEF1w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0", - "regexpu-core": "^4.5.4" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", - "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-decorators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz", - "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", - "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", - "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz", - "integrity": "sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", - "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.0.tgz", - "integrity": "sha512-EeaFdCeUULM+GPFEsf7pFcNSxM7hYjoj5fiYbyuiXobW4JhFnjAv9OWzNwHyHcKoPNpAfeRDuW6VyaXEDUBa7g==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", - "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.0.tgz", - "integrity": "sha512-AWyt3k+fBXQqt2qb9r97tn3iBwFpiv9xdAiG+Gr2HpAZpuayvbL55yWrsV3MyHvXk/4vmSiedhDRl1YI2Iy5nQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "lodash": "^4.17.11" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.3.tgz", - "integrity": "sha512-PUaIKyFUDtG6jF5DUJOfkBdwAS/kFFV3XFk7Nn0a6vR7ZT8jYw5cGtIlat77wcnd0C6ViGqo/wyNf4ZHytF/nQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-define-map": "^7.4.0", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.4.0", - "@babel/helper-split-export-declaration": "^7.4.0", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", - "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.3.tgz", - "integrity": "sha512-rVTLLZpydDFDyN4qnXdzwoVpk1oaXHIvPEOkOLyr88o7oHxVc/LyrnDx+amuBWGOwUb7D1s/uLsKBNTx08htZg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.3.tgz", - "integrity": "sha512-9Arc2I0AGynzXRR/oPdSALv3k0rM38IMFyto7kOCwb5F9sLUt2Ykdo3V9yUPR+Bgr4kb6bVEyLkPEiBhzcTeoA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.3", - "regexpu-core": "^4.5.4" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz", - "integrity": "sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", - "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.3.tgz", - "integrity": "sha512-UselcZPwVWNSURnqcfpnxtMehrb8wjXYOimlYQPBnup/Zld426YzIhNEvuRsEWVHfESIECGrxoI6L5QqzuLH5Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.3.tgz", - "integrity": "sha512-uT5J/3qI/8vACBR9I1GlAuU/JqBtWdfCrynuOkrWG6nCDieZd5przB1vfP59FRHBZQ9DC2IUfqr/xKqzOD5x0A==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", - "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz", - "integrity": "sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.3.tgz", - "integrity": "sha512-sMP4JqOTbMJMimqsSZwYWsMjppD+KRyDIUVW91pd7td0dZKAvPmhCaxhOzkzLParKwgQc7bdL9UNv+rpJB0HfA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.4.3", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.0.tgz", - "integrity": "sha512-gjPdHmqiNhVoBqus5qK60mWPp1CmYWp/tkh11mvb0rrys01HycEGD7NvvSoKXlWEfSM9TcL36CpsK8ElsADptQ==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.4.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", - "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.2.tgz", - "integrity": "sha512-NsAuliSwkL3WO2dzWTOL1oZJHm0TM8ZY8ZSxk2ANyKkt5SQlToGA4pzctmq1BEjoacurdwZ3xp2dCQWJkME0gQ==", - "dev": true, - "requires": { - "regexp-tree": "^0.1.0" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.0.tgz", - "integrity": "sha512-6ZKNgMQmQmrEX/ncuCwnnw1yVGoaOW5KpxNhoWI7pCQdA0uZ0HqHGqenCUIENAnxRjy2WwNQ30gfGdIgqJXXqw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz", - "integrity": "sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.1.0" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.3.tgz", - "integrity": "sha512-ULJYC2Vnw96/zdotCZkMGr2QVfKpIT/4/K+xWWY0MbOJyMZuk660BGkr3bEKWQrrciwz6xpmft39nA4BF7hJuA==", - "dev": true, - "requires": { - "@babel/helper-call-delegate": "^7.4.0", - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.3.tgz", - "integrity": "sha512-kEzotPuOpv6/iSlHroCDydPkKYw7tiJGKlmYp6iJn4a6C/+b2FdttlJsLKYxolYHgotTJ5G5UY5h0qey5ka3+A==", - "dev": true, - "requires": { - "regenerator-transform": "^0.13.4" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.4.3.tgz", - "integrity": "sha512-7Q61bU+uEI7bCUFReT1NKn7/X6sDQsZ7wL1sJ9IYMAO7cI+eg6x9re1cEw2fCRMbbTVyoeUKWSV1M6azEfKCfg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "resolve": "^1.8.1", - "semver": "^5.5.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", - "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz", - "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", - "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.2.0.tgz", - "integrity": "sha512-FkPix00J9A/XWXv4VoKJBMeSkyY9x/TqIh76wzcdfl57RJJcf8CehQ08uwfhCDNtRQYtHQKBTwKZDEyjE13Lwg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", - "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.3.tgz", - "integrity": "sha512-lnSNgkVjL8EMtnE8eSS7t2ku8qvKH3eqNf/IwIfnSPUqzgqYmRwzdsQWv4mNQAN9Nuo6Gz1Y0a4CSmdpu1Pp6g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.3", - "regexpu-core": "^4.5.4" - } - }, - "@babel/preset-env": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.3.4.tgz", - "integrity": "sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.2.0", - "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.3.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", - "@babel/plugin-syntax-async-generators": "^7.2.0", - "@babel/plugin-syntax-json-strings": "^7.2.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", - "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.3.4", - "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.3.4", - "@babel/plugin-transform-classes": "^7.3.4", - "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.2.0", - "@babel/plugin-transform-dotall-regex": "^7.2.0", - "@babel/plugin-transform-duplicate-keys": "^7.2.0", - "@babel/plugin-transform-exponentiation-operator": "^7.2.0", - "@babel/plugin-transform-for-of": "^7.2.0", - "@babel/plugin-transform-function-name": "^7.2.0", - "@babel/plugin-transform-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.2.0", - "@babel/plugin-transform-modules-commonjs": "^7.2.0", - "@babel/plugin-transform-modules-systemjs": "^7.3.4", - "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.3.0", - "@babel/plugin-transform-new-target": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.2.0", - "@babel/plugin-transform-parameters": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.3.4", - "@babel/plugin-transform-shorthand-properties": "^7.2.0", - "@babel/plugin-transform-spread": "^7.2.0", - "@babel/plugin-transform-sticky-regex": "^7.2.0", - "@babel/plugin-transform-template-literals": "^7.2.0", - "@babel/plugin-transform-typeof-symbol": "^7.2.0", - "@babel/plugin-transform-unicode-regex": "^7.2.0", - "browserslist": "^4.3.4", - "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.3.0" - } - }, - "@babel/runtime": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz", - "integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, - "@babel/runtime-corejs2": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.4.3.tgz", - "integrity": "sha512-anTLTF7IK8Hd5f73zpPzt875I27UaaTWARJlfMGgnmQhvEe1uNHQRKBUbXL0Gc0VEYiVzsHsTPso5XdK8NGvFg==", - "dev": true, - "requires": { - "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.2" - } - }, - "@babel/template": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz", - "integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.0", - "@babel/types": "^7.4.0" - } - }, - "@babel/traverse": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.3.tgz", - "integrity": "sha512-HmA01qrtaCwwJWpSKpA948cBvU5BrmviAief/b3AVw936DtcdsTexlbyzNuDnthwhOQ37xshn7hvQaEQk7ISYQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.0", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/types": "^7.4.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", - "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "@intervolga/optimize-cssnano-plugin": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz", - "integrity": "sha512-zN69TnSr0viRSU6cEDIcuPcP67QcpQ6uHACg58FiN9PDrU6SLyGW3MR4tiISbYxy1kDWAVPwD+XwQTWE5cigAA==", - "dev": true, - "requires": { - "cssnano": "^4.0.0", - "cssnano-preset-default": "^4.0.0", - "postcss": "^7.0.0" - } - }, - "@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", - "dev": true, - "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" - } - }, - "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", - "dev": true - }, - "@soda/friendly-errors-webpack-plugin": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz", - "integrity": "sha512-cWKrGaFX+rfbMrAxVv56DzhPNqOJPZuNIS2HGMELtgGzb+vsMzyig9mml5gZ/hr2BGtSLV+dP2LUEuAL8aG2mQ==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "error-stack-parser": "^2.0.0", - "string-width": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", - "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/node": { - "version": "11.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.0.tgz", - "integrity": "sha512-rx29MMkRdVmzunmiA4lzBYJNnXsW/PhG4kMBy2ATsYaDjGGR75dCFEVVROKpNwlVdcUX3xxlghKQOeDPBJobng==", - "dev": true - }, - "@types/q": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", - "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", - "dev": true - }, - "@vue/babel-helper-vue-jsx-merge-props": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0-beta.3.tgz", - "integrity": "sha512-cbFQnd3dDPsfWuxbWW2phynX2zsckwC4GfAkcE1QH1lZL2ZAD2V97xY3BmvTowMkjeFObRKQt1P3KKA6AoB0hQ==", - "dev": true - }, - "@vue/babel-plugin-transform-vue-jsx": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.0.0-beta.3.tgz", - "integrity": "sha512-yn+j2B/2aEagaxXrMSK3qcAJnlidfXg9v+qmytqrjUXc4zfi8QVC/b4zCev1FDmTip06/cs/csENA4law6Xhpg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.2.0", - "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0-beta.3", - "html-tags": "^2.0.0", - "lodash.kebabcase": "^4.1.1", - "svg-tags": "^1.0.0" - } - }, - "@vue/babel-preset-app": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-3.5.5.tgz", - "integrity": "sha512-kK2DkQ+uGOjDnVjgn8A3Rczic55rI42nMESmwWfrfjssPeFQLGqiJixk9eQccHNBjyHcl3D+UOWx+EHQbpPByg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/plugin-proposal-class-properties": "^7.0.0", - "@babel/plugin-proposal-decorators": "^7.1.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/plugin-transform-runtime": "^7.4.0", - "@babel/preset-env": "^7.0.0 < 7.4.0", - "@babel/runtime": "^7.0.0", - "@babel/runtime-corejs2": "^7.2.0", - "@vue/babel-preset-jsx": "^1.0.0-beta.2", - "babel-plugin-dynamic-import-node": "^2.2.0", - "core-js": "^2.6.5" - } - }, - "@vue/babel-preset-jsx": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/@vue/babel-preset-jsx/-/babel-preset-jsx-1.0.0-beta.3.tgz", - "integrity": "sha512-qMKGRorTI/0nE83nLEM7MyQiBZUqc62sZyjkBdVaaU7S61MHI8RKHPtbLMMZlWXb2NCJ0fQci8xJWUK5JE+TFA==", - "dev": true, - "requires": { - "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0-beta.3", - "@vue/babel-plugin-transform-vue-jsx": "^1.0.0-beta.3", - "@vue/babel-sugar-functional-vue": "^1.0.0-beta.3", - "@vue/babel-sugar-inject-h": "^1.0.0-beta.3", - "@vue/babel-sugar-v-model": "^1.0.0-beta.3", - "@vue/babel-sugar-v-on": "^1.0.0-beta.3" - } - }, - "@vue/babel-sugar-functional-vue": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.0.0-beta.3.tgz", - "integrity": "sha512-CBIa0sQWn3vfBS2asfTgv0WwdyKvNTKtE/cCfulZ7MiewLBh0RlvvSmdK9BIMTiHErdeZNSGUGlU6JuSHLyYkQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0" - } - }, - "@vue/babel-sugar-inject-h": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.0.0-beta.3.tgz", - "integrity": "sha512-HKMBMmFfdK9GBp3rX2bHIwILBdgc5F3ahmCB72keJxzaAQrgDAnD+ho70exUge+inAGlNF34WsQcGPElTf9QZg==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0" - } - }, - "@vue/babel-sugar-v-model": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.0.0-beta.3.tgz", - "integrity": "sha512-et39eTEh7zW4wfZoSl9Jf0/n2r9OTT8U02LtSbXsjgYcqaDQFusN0+n7tw4bnOqvnnSVjEp7bVsQCWwykC3Wgg==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0", - "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0-beta.3", - "@vue/babel-plugin-transform-vue-jsx": "^1.0.0-beta.3", - "camelcase": "^5.0.0", - "html-tags": "^2.0.0", - "svg-tags": "^1.0.0" - } - }, - "@vue/babel-sugar-v-on": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.0.0-beta.3.tgz", - "integrity": "sha512-F+GapxCiy50jf2Q2B4exw+KYBzlGdeKMAMW1Dbvb0Oa59SA0CH6tsUOIAsXb0A05jwwg/of0LaVeo+4aLefVxQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0", - "@vue/babel-plugin-transform-vue-jsx": "^1.0.0-beta.3", - "camelcase": "^5.0.0" - } - }, - "@vue/cli-overlay": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-3.5.1.tgz", - "integrity": "sha512-DqzfkbKJfuzcNbJouA7ZaLX77xn7FCcVUJaPYVH8qm3pNhIz2tmbfN6WVBLU8XC5FNFFWzLjHmg9rpaEBq7RCA==", - "dev": true - }, - "@vue/cli-plugin-babel": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-3.5.5.tgz", - "integrity": "sha512-ew+EWL2ur234wqQfI1AvWcghjWQ2PlSVpUAfnDlejplDzXSHz7455IIwN7Auhs7siEbls3EVd/bl/ieKGjwY5g==", - "dev": true, - "requires": { - "@babel/core": "^7.0.0", - "@vue/babel-preset-app": "^3.5.5", - "@vue/cli-shared-utils": "^3.5.1", - "babel-loader": "^8.0.5", - "webpack": ">=4 < 4.29" - } - }, - "@vue/cli-plugin-eslint": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-3.5.1.tgz", - "integrity": "sha512-0RSF3LPOXUIgArrg06HK1Yg6wcR348wssVX3pG41zxDAGTmfe8TpL2XPiP/KinrYwQLSUBPbr8zdf9Hb1+Tv2w==", - "dev": true, - "requires": { - "@vue/cli-shared-utils": "^3.5.1", - "babel-eslint": "^10.0.1", - "eslint": "^4.19.1", - "eslint-loader": "^2.1.2", - "eslint-plugin-vue": "^4.7.1", - "globby": "^9.0.0", - "webpack": ">=4 < 4.29" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "optional": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "optional": true - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "eslint": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", - "dev": true, - "optional": true, - "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" - } - }, - "eslint-plugin-vue": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-4.7.1.tgz", - "integrity": "sha512-esETKhVMI7Vdli70Wt4bvAwnZBJeM0pxVX9Yb0wWKxdCJc2EADalVYK/q2FzMw8oKN0wPMdqVCKS8kmR89recA==", - "dev": true, - "optional": true, - "requires": { - "vue-eslint-parser": "^2.0.3" - } - }, - "eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", - "dev": true, - "optional": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true, - "optional": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true, - "optional": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "optional": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true, - "optional": true - } - } - }, - "@vue/cli-service": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-3.5.3.tgz", - "integrity": "sha512-28MDRVZe+yebtiSJHfYS6fOvPN1P8DX4BIsDp4rLqZxc7l8yoG6pBWyv9ENT4WO9gAXRGzEsKcJ5qLtXLvQeYQ==", - "dev": true, - "requires": { - "@intervolga/optimize-cssnano-plugin": "^1.0.5", - "@soda/friendly-errors-webpack-plugin": "^1.7.1", - "@vue/cli-overlay": "^3.5.1", - "@vue/cli-shared-utils": "^3.5.1", - "@vue/component-compiler-utils": "^2.6.0", - "@vue/preload-webpack-plugin": "^1.1.0", - "@vue/web-component-wrapper": "^1.2.0", - "acorn": "^6.1.0", - "acorn-walk": "^6.1.1", - "address": "^1.0.3", - "autoprefixer": "^9.4.8", - "cache-loader": "^2.0.1", - "case-sensitive-paths-webpack-plugin": "^2.2.0", - "chalk": "^2.4.2", - "clipboardy": "^1.2.3", - "cliui": "^4.1.0", - "copy-webpack-plugin": "^4.6.0", - "css-loader": "^1.0.1", - "cssnano": "^4.1.10", - "debug": "^4.1.1", - "dotenv": "^6.2.0", - "dotenv-expand": "^4.2.0", - "escape-string-regexp": "^1.0.5", - "file-loader": "^3.0.1", - "fs-extra": "^7.0.1", - "globby": "^9.0.0", - "hash-sum": "^1.0.2", - "html-webpack-plugin": "^3.2.0", - "launch-editor-middleware": "^2.2.1", - "lodash.defaultsdeep": "^4.6.0", - "lodash.mapvalues": "^4.6.0", - "lodash.transform": "^4.6.0", - "mini-css-extract-plugin": "^0.5.0", - "minimist": "^1.2.0", - "ora": "^3.1.0", - "portfinder": "^1.0.20", - "postcss-loader": "^3.0.0", - "read-pkg": "^4.0.1", - "semver": "^5.6.0", - "slash": "^2.0.0", - "source-map-url": "^0.4.0", - "ssri": "^6.0.1", - "string.prototype.padend": "^3.0.0", - "terser-webpack-plugin": "^1.2.2", - "thread-loader": "^2.1.2", - "url-loader": "^1.1.2", - "vue-loader": "^15.6.4", - "webpack": ">=4 < 4.29", - "webpack-bundle-analyzer": "^3.0.4", - "webpack-chain": "^4.11.0", - "webpack-dev-server": "^3.2.0", - "webpack-merge": "^4.2.1", - "yorkie": "^2.0.0" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - } - } - }, - "@vue/cli-shared-utils": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-3.5.1.tgz", - "integrity": "sha512-hCB7UbKeeC41w2Q8+Q7jmw3gHdq+ltRqp80S3uDRRGxwiOhxrSmdBHMzKUjh01L8bXOBRgvLey+BERi1Nj9n6Q==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "execa": "^1.0.0", - "joi": "^14.3.0", - "launch-editor": "^2.2.1", - "lru-cache": "^5.1.1", - "node-ipc": "^9.1.1", - "opn": "^5.3.0", - "ora": "^3.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.7", - "semver": "^5.5.0", - "string.prototype.padstart": "^3.0.0" - } - }, - "@vue/component-compiler-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-2.6.0.tgz", - "integrity": "sha512-IHjxt7LsOFYc0DkTncB7OXJL7UzwOLPPQCfEUNyxL2qt+tF12THV+EO33O1G2Uk4feMSWua3iD39Itszx0f0bw==", - "dev": true, - "requires": { - "consolidate": "^0.15.1", - "hash-sum": "^1.0.2", - "lru-cache": "^4.1.2", - "merge-source-map": "^1.1.0", - "postcss": "^7.0.14", - "postcss-selector-parser": "^5.0.0", - "prettier": "1.16.3", - "source-map": "~0.6.1", - "vue-template-es2015-compiler": "^1.9.0" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "@vue/preload-webpack-plugin": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.0.tgz", - "integrity": "sha512-rcn2KhSHESBFMPj5vc5X2pI9bcBNQQixvJXhD5gZ4rN2iym/uH2qfDSQfUS5+qwiz0a85TCkeUs6w6jxFDudbw==", - "dev": true - }, - "@vue/web-component-wrapper": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vue/web-component-wrapper/-/web-component-wrapper-1.2.0.tgz", - "integrity": "sha512-Xn/+vdm9CjuC9p3Ae+lTClNutrVhsXpzxvoTXXtoys6kVRX9FkueSUAqSWAyZntmVLlR4DosBV4pH8y5Z/HbUw==", - "dev": true - }, - "@webassemblyjs/ast": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", - "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==", - "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/wast-parser": "1.7.11" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz", - "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz", - "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz", - "integrity": "sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz", - "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.7.11" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz", - "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz", - "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==", - "dev": true - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz", - "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz", - "integrity": "sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz", - "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz", - "integrity": "sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.1" - } - }, - "@webassemblyjs/utf8": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz", - "integrity": "sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz", - "integrity": "sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/helper-wasm-section": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11", - "@webassemblyjs/wasm-opt": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11", - "@webassemblyjs/wast-printer": "1.7.11" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz", - "integrity": "sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/ieee754": "1.7.11", - "@webassemblyjs/leb128": "1.7.11", - "@webassemblyjs/utf8": "1.7.11" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz", - "integrity": "sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz", - "integrity": "sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-api-error": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/ieee754": "1.7.11", - "@webassemblyjs/leb128": "1.7.11", - "@webassemblyjs/utf8": "1.7.11" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz", - "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/floating-point-hex-parser": "1.7.11", - "@webassemblyjs/helper-api-error": "1.7.11", - "@webassemblyjs/helper-code-frame": "1.7.11", - "@webassemblyjs/helper-fsm": "1.7.11", - "@xtuc/long": "4.2.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz", - "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/wast-parser": "1.7.11", - "@xtuc/long": "4.2.1" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", - "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", - "dev": true - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "dev": true, - "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", - "dev": true, - "requires": { - "acorn": "^5.0.0" - } - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "optional": true, - "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true, - "optional": true - } - } - }, - "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", - "dev": true - }, - "address": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz", - "integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg==", - "dev": true - }, - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "ajv-keywords": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", - "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", - "dev": true - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "arch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", - "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-filter": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", - "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "array-map": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", - "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", - "dev": true - }, - "array-reduce": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", - "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "async-each": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.2.tgz", - "integrity": "sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg==", - "dev": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "autoprefixer": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.5.0.tgz", - "integrity": "sha512-hMKcyHsZn5+qL6AUeP3c8OyuteZ4VaUlg+fWbyl8z7PqsKHF/Bf8/px3K6AT8aMzDkBo8Bc11245MM+itDBOxQ==", - "dev": true, - "requires": { - "browserslist": "^4.4.2", - "caniuse-lite": "^1.0.30000947", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.14", - "postcss-value-parser": "^3.3.1" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true - }, - "axios": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", - "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", - "requires": { - "follow-redirects": "^1.3.0", - "is-buffer": "^1.1.5" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-eslint": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz", - "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "eslint-scope": "3.7.1", - "eslint-visitor-keys": "^1.0.0" - }, - "dependencies": { - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - } - } - }, - "babel-loader": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.5.tgz", - "integrity": "sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw==", - "dev": true, - "requires": { - "find-cache-dir": "^2.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "util.promisify": "^1.0.0" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.2.0.tgz", - "integrity": "sha512-fP899ELUnTaBcIzmrW7nniyqqdYWrWuJUyPWHxFa/c7r7hS6KC8FscNfLlBNIoPSc55kYMGEEKjPjJGCLbE1qA==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bfj": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.1.tgz", - "integrity": "sha512-+GUNvzHR4nRyGybQc2WpNJL4MJazMuvf92ueIyA0bIkPRwhhQu3IfZQ2PSoVPpCBJfmoSdOxu5rnotfFLlvYRQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "check-types": "^7.3.0", - "hoopy": "^0.1.2", - "tryer": "^1.0.0" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - }, - "dependencies": { - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - } - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.4.tgz", - "integrity": "sha512-rAjx494LMjqKnMPhFkuLmLp8JWEX0o8ADTGeAbOqaF+XCvYLreZrG5uVjnPBlAQ8REZK4pzXGvp0bWgrFtKaag==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000955", - "electron-to-chromium": "^1.3.122", - "node-releases": "^1.1.13" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cache-loader": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-2.0.1.tgz", - "integrity": "sha512-V99T3FOynmGx26Zom+JrVBytLBsmUCzVG2/4NnUKgvXN4bEV42R1ERl1IyiH/cvFIDA1Ytq2lPZ9tXDSahcQpQ==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.0", - "normalize-path": "^3.0.0", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", - "dev": true - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - }, - "dependencies": { - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - } - } - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "optional": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true, - "optional": true - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true, - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } - }, - "camelcase": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.0.tgz", - "integrity": "sha512-Y05ICatFYPAfykDIB7VdwSJ0LUl1yq/BwO2OpyGGLjiRe1fgzTwVypPiWnzkGFOVFHXrCXUNBl86bpjBhZWSJg==", - "dev": true - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30000957", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000957.tgz", - "integrity": "sha512-8wxNrjAzyiHcLXN/iunskqQnJquQQ6VX8JHfW5kLgAPRSiSuKZiNfmIkP5j7jgyXqAQBSoXyJxfnbCFS0ThSiQ==", - "dev": true - }, - "case-sensitive-paths-webpack-plugin": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.2.0.tgz", - "integrity": "sha512-u5ElzokS8A1pm9vM3/iDgTcI3xqHxuCao94Oz8etI3cf0Tio0p8izkDYbTIn09uP3yUUr6+veaE6IkjnTYS46g==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true, - "optional": true - }, - "check-types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-7.4.0.tgz", - "integrity": "sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg==", - "dev": true - }, - "chokidar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", - "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", - "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true, - "optional": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-spinners": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.0.0.tgz", - "integrity": "sha512-yiEBmhaKPPeBj7wWm4GEdtPZK940p9pl3EANIrnJ3JnvWyrPjcFcsEq6qRUuQ7fzB0+Y82ld3p6B34xo95foWw==", - "dev": true - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "clipboardy": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.3.tgz", - "integrity": "sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA==", - "dev": true, - "requires": { - "arch": "^2.1.0", - "execa": "^0.8.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", - "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dev": true, - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.0.tgz", - "integrity": "sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg==", - "dev": true, - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "compressible": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", - "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", - "dev": true, - "requires": { - "mime-db": ">= 1.38.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } - }, - "consolidate": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", - "dev": true, - "requires": { - "bluebird": "^3.1.1" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", - "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", - "dev": true, - "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "globby": "^7.1.1", - "is-glob": "^4.0.0", - "loader-utils": "^1.1.0", - "minimatch": "^3.0.4", - "p-limit": "^1.0.0", - "serialize-javascript": "^1.4.0" - }, - "dependencies": { - "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cosmiconfig": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.0.tgz", - "integrity": "sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g==", - "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.0", - "parse-json": "^4.0.0" - } - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-declaration-sorter": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", - "dev": true, - "requires": { - "postcss": "^7.0.1", - "timsort": "^0.3.0" - } - }, - "css-loader": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.1.tgz", - "integrity": "sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "css-selector-tokenizer": "^0.7.0", - "icss-utils": "^2.1.0", - "loader-utils": "^1.0.2", - "lodash": "^4.17.11", - "postcss": "^6.0.23", - "postcss-modules-extract-imports": "^1.2.0", - "postcss-modules-local-by-default": "^1.2.0", - "postcss-modules-scope": "^1.1.0", - "postcss-modules-values": "^1.3.0", - "postcss-value-parser": "^3.3.0", - "source-list-map": "^2.0.0" - }, - "dependencies": { - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "css-select": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz", - "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^2.1.2", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "dev": true - }, - "css-selector-tokenizer": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", - "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", - "dev": true, - "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" - }, - "dependencies": { - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - } - } - }, - "css-tree": { - "version": "1.0.0-alpha.28", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz", - "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==", - "dev": true, - "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" - } - }, - "css-unit-converter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz", - "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=", - "dev": true - }, - "css-url-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz", - "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=", - "dev": true - }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", - "dev": true - }, - "cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", - "dev": true - }, - "cssnano": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", - "dev": true, - "requires": { - "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.7", - "is-resolvable": "^1.0.0", - "postcss": "^7.0.0" - } - }, - "cssnano-preset-default": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", - "dev": true, - "requires": { - "css-declaration-sorter": "^4.0.1", - "cssnano-util-raw-cache": "^4.0.1", - "postcss": "^7.0.0", - "postcss-calc": "^7.0.1", - "postcss-colormin": "^4.0.3", - "postcss-convert-values": "^4.0.1", - "postcss-discard-comments": "^4.0.2", - "postcss-discard-duplicates": "^4.0.2", - "postcss-discard-empty": "^4.0.1", - "postcss-discard-overridden": "^4.0.1", - "postcss-merge-longhand": "^4.0.11", - "postcss-merge-rules": "^4.0.3", - "postcss-minify-font-values": "^4.0.2", - "postcss-minify-gradients": "^4.0.2", - "postcss-minify-params": "^4.0.2", - "postcss-minify-selectors": "^4.0.2", - "postcss-normalize-charset": "^4.0.1", - "postcss-normalize-display-values": "^4.0.2", - "postcss-normalize-positions": "^4.0.2", - "postcss-normalize-repeat-style": "^4.0.2", - "postcss-normalize-string": "^4.0.2", - "postcss-normalize-timing-functions": "^4.0.2", - "postcss-normalize-unicode": "^4.0.1", - "postcss-normalize-url": "^4.0.1", - "postcss-normalize-whitespace": "^4.0.2", - "postcss-ordered-values": "^4.1.2", - "postcss-reduce-initial": "^4.0.3", - "postcss-reduce-transforms": "^4.0.2", - "postcss-svgo": "^4.0.2", - "postcss-unique-selectors": "^4.0.1" - } - }, - "cssnano-util-get-arguments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", - "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", - "dev": true - }, - "cssnano-util-get-match": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", - "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", - "dev": true - }, - "cssnano-util-raw-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "cssnano-util-same-parent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", - "dev": true - }, - "csso": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", - "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", - "dev": true, - "requires": { - "css-tree": "1.0.0-alpha.29" - }, - "dependencies": { - "css-tree": { - "version": "1.0.0-alpha.29", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", - "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", - "dev": true, - "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" - } - } - } - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", - "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", - "dev": true, - "requires": { - "xregexp": "4.0.0" - } - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "deepmerge": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", - "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", - "dev": true - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - } - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "dev": true, - "requires": { - "path-type": "^3.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "optional": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "requires": { - "utila": "~0.4" - } - }, - "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "dev": true, - "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "dotenv": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", - "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==", - "dev": true - }, - "dotenv-expand": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-4.2.0.tgz", - "integrity": "sha1-3vHxyl1gWdJKdm5YeULCEQbOEnU=", - "dev": true - }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "easy-stack": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", - "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "ejs": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", - "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.122", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.122.tgz", - "integrity": "sha512-3RKoIyCN4DhP2dsmleuFvpJAIDOseWH88wFYBzb22CSwoFDSWRc4UAMfrtc9h8nBdJjTNIN3rogChgOy6eFInw==", - "dev": true - }, - "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "error-stack-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.2.tgz", - "integrity": "sha512-E1fPutRDdIj/hohG0UpT5mayXNCxXP9d+snxFsPU9X0XgccOumKraa3juDMwTUyi7+Bu5+mCGagjg4IYeNbOdw==", - "dev": true, - "requires": { - "stackframe": "^1.0.4" - } - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - }, - "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", - "dev": true - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", - "dev": true, - "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", - "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "inquirer": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", - "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.11", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "table": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", - "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==", - "dev": true, - "requires": { - "ajv": "^6.9.1", - "lodash": "^4.17.11", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - } - } - }, - "eslint-loader": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.1.2.tgz", - "integrity": "sha512-rA9XiXEOilLYPOIInvVH5S/hYfyTPyxag6DZhoQOduM+3TkghAEQ3VcFO8VnX4J4qg/UIBzp72aOf/xvYmpmsg==", - "dev": true, - "requires": { - "loader-fs-cache": "^1.0.0", - "loader-utils": "^1.0.2", - "object-assign": "^4.0.1", - "object-hash": "^1.1.4", - "rimraf": "^2.6.1" - } - }, - "eslint-plugin-vue": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-5.2.2.tgz", - "integrity": "sha512-CtGWH7IB0DA6BZOwcV9w9q3Ri6Yuo8qMjx05SmOGJ6X6E0Yo3y9E/gQ5tuNxg2dEt30tRnBoFTbvtmW9iEoyHA==", - "dev": true, - "requires": { - "vue-eslint-parser": "^5.0.0" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - }, - "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", - "dev": true - }, - "espree": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", - "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", - "dev": true, - "requires": { - "acorn": "^6.0.2", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "vue-eslint-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz", - "integrity": "sha512-JlHVZwBBTNVvzmifwjpZYn0oPWH2SgWv5dojlZBsrhablDu95VFD+hriB1rQGwbD+bms6g+rAFhQHk6+NyiS6g==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "eslint-scope": "^4.0.0", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.1.0", - "esquery": "^1.0.1", - "lodash": "^4.17.11" - } - } - } - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, - "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "dev": true, - "optional": true, - "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "event-pubsub": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", - "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", - "dev": true - }, - "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", - "dev": true - }, - "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", - "dev": true - }, - "eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", - "dev": true, - "requires": { - "original": "^1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "optional": true, - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "fast-glob": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.6.tgz", - "integrity": "sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w==", - "dev": true, - "requires": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.1.2", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.3", - "micromatch": "^3.1.10" - } - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "optional": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, - "file-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", - "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "filesize": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", - "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", - "dev": true - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", - "dev": true, - "optional": true, - "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" - } - }, - "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", - "dev": true - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "follow-redirects": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", - "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", - "requires": { - "debug": "^3.2.6" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", - "dev": true - }, - "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", - "dev": true - }, - "globby": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", - "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "array-union": "^1.0.2", - "dir-glob": "^2.2.2", - "fast-glob": "^2.2.6", - "glob": "^7.1.3", - "ignore": "^4.0.3", - "pify": "^4.0.1", - "slash": "^2.0.0" - }, - "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "gzip-size": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz", - "integrity": "sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA==", - "dev": true, - "requires": { - "duplexer": "^0.1.1", - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "handle-thing": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", - "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==", - "dev": true - }, - "hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "dev": true - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, - "html-comment-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", - "dev": true - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "html-minifier": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", - "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", - "dev": true, - "requires": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.2.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - } - } - }, - "html-tags": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", - "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", - "dev": true - }, - "html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", - "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", - "dev": true, - "requires": { - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "tapable": "^1.0.0", - "toposort": "^1.0.0", - "util.promisify": "1.0.0" - }, - "dependencies": { - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - } - } - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "http-parser-js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", - "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", - "dev": true - }, - "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", - "dev": true, - "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", - "dev": true, - "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, - "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", - "dev": true, - "requires": { - "postcss": "^6.0.1" - }, - "dependencies": { - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "import-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", - "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", - "dev": true, - "requires": { - "import-from": "^2.1.0" - } - }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - }, - "dependencies": { - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - } - } - }, - "import-from": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", - "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - } - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", - "dev": true, - "optional": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "internal-ip": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.2.0.tgz", - "integrity": "sha512-ZY8Rk+hlvFeuMmG5uH1MXhhdeMntmIaxaInvAmzMq/SHV8rv4Kh+6GiQNNDQd0wZFrcO+FiTBo8lui/osKOyJw==", - "dev": true, - "requires": { - "default-gateway": "^4.0.1", - "ipaddr.js": "^1.9.0" - }, - "dependencies": { - "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", - "dev": true - } - } - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", - "dev": true - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", - "dev": true, - "requires": { - "ci-info": "^1.5.0" - } - }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dev": true, - "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "dev": true, - "requires": { - "punycode": "2.x.x" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "javascript-stringify": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz", - "integrity": "sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM=", - "dev": true - }, - "joi": { - "version": "14.3.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-14.3.1.tgz", - "integrity": "sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==", - "dev": true, - "requires": { - "hoek": "6.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" - } - }, - "js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", - "dev": true - }, - "js-message": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", - "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", - "dev": true - }, - "js-queue": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", - "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", - "dev": true, - "requires": { - "easy-stack": "^1.0.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz", - "integrity": "sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "launch-editor": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.2.1.tgz", - "integrity": "sha512-On+V7K2uZK6wK7x691ycSUbLD/FyKKelArkbaAMSSJU8JmqmhwN2+mnJDNINuJWSrh2L0kDk+ZQtbC/gOWUwLw==", - "dev": true, - "requires": { - "chalk": "^2.3.0", - "shell-quote": "^1.6.1" - } - }, - "launch-editor-middleware": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.2.1.tgz", - "integrity": "sha512-s0UO2/gEGiCgei3/2UN3SMuUj1phjQN8lcpnvgLSz26fAzNWPQ6Nf/kF5IFClnfU2ehp6LrmKdMU/beveO+2jg==", - "dev": true, - "requires": { - "launch-editor": "^2.2.1" - } - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "loader-fs-cache": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz", - "integrity": "sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw==", - "dev": true, - "requires": { - "find-cache-dir": "^0.1.1", - "mkdirp": "0.5.1" - }, - "dependencies": { - "find-cache-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", - "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true, - "requires": { - "find-up": "^1.0.0" - } - } - } - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "lodash.defaultsdeep": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz", - "integrity": "sha1-vsECT4WxvZbL6kBbI8FK1kQ6b4E=", - "dev": true - }, - "lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", - "dev": true - }, - "lodash.mapvalues": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", - "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.transform": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz", - "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - }, - "loglevel": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", - "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "mdn-data": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", - "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - } - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "merge2": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", - "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", - "dev": true - }, - "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", - "dev": true - }, - "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", - "dev": true, - "requires": { - "mime-db": "~1.38.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", - "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - }, - "dependencies": { - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "neo-async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", - "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "^1.1.1" - } - }, - "node-forge": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", - "dev": true - }, - "node-ipc": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", - "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", - "dev": true, - "requires": { - "event-pubsub": "4.3.0", - "js-message": "1.0.5", - "js-queue": "2.0.0" - } - }, - "node-libs-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", - "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "node-releases": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.13.tgz", - "integrity": "sha512-fKZGviSXR6YvVPyc011NHuJDSD8gFQvLPmc2d2V3BS4gr52ycyQ1Xzs7a8B+Ax3Ni/W+5h1h4SqmzeoA8WZRmA==", - "dev": true, - "requires": { - "semver": "^5.3.0" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-hash": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", - "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", - "dev": true - }, - "object-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", - "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", - "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "opener": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", - "dev": true - }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "ora": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", - "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-spinners": "^2.0.0", - "log-symbols": "^2.2.0", - "strip-ansi": "^5.2.0", - "wcwidth": "^1.0.1" - } - }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", - "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", - "dev": true - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true, - "requires": { - "no-case": "^2.2.0" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - }, - "dependencies": { - "callsites": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", - "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==", - "dev": true - } - } - }, - "parse-asn1": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", - "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true, - "optional": true - }, - "portfinder": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", - "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", - "dev": true, - "requires": { - "async": "^1.5.2", - "debug": "^2.2.0", - "mkdirp": "0.5.x" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", - "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-calc": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz", - "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", - "dev": true, - "requires": { - "css-unit-converter": "^1.1.1", - "postcss": "^7.0.5", - "postcss-selector-parser": "^5.0.0-rc.4", - "postcss-value-parser": "^3.3.1" - } - }, - "postcss-colormin": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-convert-values": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-load-config": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", - "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", - "dev": true, - "requires": { - "cosmiconfig": "^4.0.0", - "import-cwd": "^2.0.0" - }, - "dependencies": { - "cosmiconfig": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", - "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0", - "require-from-string": "^2.0.1" - } - } - } - }, - "postcss-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", - "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "postcss": "^7.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "postcss-merge-longhand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", - "dev": true, - "requires": { - "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" - } - }, - "postcss-merge-rules": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "cssnano-util-same-parent": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0", - "vendors": "^1.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-minify-font-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-minify-gradients": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-minify-params": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "browserslist": "^4.0.0", - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "uniqs": "^2.0.0" - } - }, - "postcss-minify-selectors": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-modules-extract-imports": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", - "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", - "dev": true, - "requires": { - "postcss": "^6.0.1" - }, - "dependencies": { - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "dev": true, - "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - }, - "dependencies": { - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "dev": true, - "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - }, - "dependencies": { - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "dev": true, - "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" - }, - "dependencies": { - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-normalize-charset": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-normalize-display-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-positions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-repeat-style": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-string": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", - "dev": true, - "requires": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-timing-functions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-unicode": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", - "dev": true, - "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-normalize-whitespace": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-ordered-values": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-reduce-initial": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - } - }, - "postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "dev": true, - "requires": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-svgo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", - "dev": true, - "requires": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" - } - }, - "postcss-unique-selectors": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prettier": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.3.tgz", - "integrity": "sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw==", - "dev": true - }, - "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "dev": true, - "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" - } - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "read-pkg": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", - "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=", - "dev": true, - "requires": { - "normalize-package-data": "^2.3.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz", - "integrity": "sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regenerator-runtime": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", - "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==", - "dev": true - }, - "regenerator-transform": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz", - "integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==", - "dev": true, - "requires": { - "private": "^0.1.6" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexp-tree": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.5.tgz", - "integrity": "sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ==", - "dev": true - }, - "regexpp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", - "dev": true, - "optional": true - }, - "regexpu-core": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", - "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.0.2", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", - "dev": true - }, - "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "renderkid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", - "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", - "dev": true, - "requires": { - "css-select": "^1.1.0", - "dom-converter": "^0.2", - "htmlparser2": "^3.3.0", - "strip-ansi": "^3.0.0", - "utila": "^0.4.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "request-promise-native": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", - "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", - "dev": true, - "requires": { - "request-promise-core": "1.1.2", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "optional": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - } - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - } - } - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true, - "optional": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", - "dev": true - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "dev": true, - "optional": true, - "requires": { - "rx-lite": "*" - } - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selfsigned": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", - "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", - "dev": true, - "requires": { - "node-forge": "0.7.5" - } - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", - "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==", - "dev": true - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shell-quote": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", - "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", - "dev": true, - "requires": { - "array-filter": "~0.0.0", - "array-map": "~0.0.0", - "array-reduce": "~0.0.0", - "jsonify": "~0.0.0" - } - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dev": true, - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - } - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "optional": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "dev": true, - "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" - } - }, - "sockjs-client": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", - "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", - "dev": true, - "requires": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - } - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.11", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.11.tgz", - "integrity": "sha512-//sajEx/fGL3iw6fltKMdPvy8kL3kJ2O3iuYlRoT3k9Kb4BjOoZ+BZzaNHeuaruSt+Kf3Zk9tnfAQg9/AJqUVQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", - "dev": true - }, - "spdy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", - "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - }, - "dependencies": { - "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "dev": true - }, - "stackframe": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz", - "integrity": "sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw==", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string.prototype.padend": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", - "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.4.3", - "function-bind": "^1.0.2" - } - }, - "string.prototype.padstart": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.padstart/-/string.prototype.padstart-3.0.0.tgz", - "integrity": "sha1-W8+tOfRkm7LQMSkuGbzwtRDUskI=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.4.3", - "function-bind": "^1.0.2" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "stylehacks": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", - "dev": true - }, - "svgo": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.2.0.tgz", - "integrity": "sha512-xBfxJxfk4UeVN8asec9jNxHiv3UAMv/ujwBWGYvQhhMb2u3YTGKkiybPcLFDLq7GLLWE9wa73e0/m8L5nTzQbw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.28", - "css-url-regex": "^1.1.0", - "csso": "^3.5.1", - "js-yaml": "^3.12.0", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - } - }, - "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", - "dev": true, - "optional": true, - "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "optional": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true, - "optional": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true, - "optional": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true, - "optional": true - } - } - }, - "tapable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", - "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", - "dev": true - }, - "terser": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", - "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", - "dev": true, - "requires": { - "commander": "^2.19.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.10" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "terser-webpack-plugin": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz", - "integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==", - "dev": true, - "requires": { - "cacache": "^11.0.2", - "find-cache-dir": "^2.0.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "terser": "^3.16.1", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - }, - "dependencies": { - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "thread-loader": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-2.1.2.tgz", - "integrity": "sha512-7xpuc9Ifg6WU+QYw/8uUqNdRwMD+N5gjwHKMqETrs96Qn+7BHwECpt2Brzr4HFlf4IAkZsayNhmGdbkBsTJ//w==", - "dev": true, - "requires": { - "loader-runner": "^2.3.1", - "loader-utils": "^1.1.0", - "neo-async": "^2.6.0" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "thunky": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", - "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", - "dev": true - }, - "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "topo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", - "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", - "dev": true, - "requires": { - "hoek": "6.x.x" - } - }, - "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", - "dev": true - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", - "dev": true - }, - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", - "dev": true - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "uglify-js": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", - "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", - "dev": true, - "requires": { - "commander": "~2.19.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", - "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", - "dev": true - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", - "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "upath": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", - "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", - "dev": true - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-loader": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", - "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "mime": "^2.0.3", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "url-parse": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", - "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", - "dev": true, - "requires": { - "querystringify": "^2.0.0", - "requires-port": "^1.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "vendors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", - "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "vue": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz", - "integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==" - }, - "vue-eslint-parser": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz", - "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==", - "dev": true, - "optional": true, - "requires": { - "debug": "^3.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.2", - "esquery": "^1.0.0", - "lodash": "^4.17.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", - "dev": true, - "optional": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - } - } - }, - "vue-hot-reload-api": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz", - "integrity": "sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g==", - "dev": true - }, - "vue-loader": { - "version": "15.7.0", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.7.0.tgz", - "integrity": "sha512-x+NZ4RIthQOxcFclEcs8sXGEWqnZHodL2J9Vq+hUz+TDZzBaDIh1j3d9M2IUlTjtrHTZy4uMuRdTi8BGws7jLA==", - "dev": true, - "requires": { - "@vue/component-compiler-utils": "^2.5.1", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "vue-hot-reload-api": "^2.3.0", - "vue-style-loader": "^4.1.0" - } - }, - "vue-router": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.2.tgz", - "integrity": "sha512-opKtsxjp9eOcFWdp6xLQPLmRGgfM932Tl56U9chYTnoWqKxQ8M20N7AkdEbM5beUh6wICoFGYugAX9vQjyJLFg==" - }, - "vue-style-loader": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", - "integrity": "sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ==", - "dev": true, - "requires": { - "hash-sum": "^1.0.2", - "loader-utils": "^1.0.2" - } - }, - "vue-template-compiler": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz", - "integrity": "sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg==", - "dev": true, - "requires": { - "de-indent": "^1.0.2", - "he": "^1.1.0" - } - }, - "vue-template-es2015-compiler": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", - "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", - "dev": true - }, - "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "dev": true, - "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webpack": { - "version": "4.28.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.28.4.tgz", - "integrity": "sha512-NxjD61WsK/a3JIdwWjtIpimmvE6UrRi3yG54/74Hk9rwNj5FPkA4DJCf1z4ByDWLkvZhTZE+P3C/eh6UD5lDcw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-module-context": "1.7.11", - "@webassemblyjs/wasm-edit": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11", - "acorn": "^5.6.2", - "acorn-dynamic-import": "^3.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^1.0.0", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.0", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "micromatch": "^3.1.8", - "mkdirp": "~0.5.0", - "neo-async": "^2.5.0", - "node-libs-browser": "^2.0.0", - "schema-utils": "^0.4.4", - "tapable": "^1.1.0", - "terser-webpack-plugin": "^1.1.0", - "watchpack": "^1.5.0", - "webpack-sources": "^1.3.0" - } - }, - "webpack-bundle-analyzer": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.1.0.tgz", - "integrity": "sha512-nyDyWEs7C6DZlgvu1pR1zzJfIWSiGPbtaByZr8q+Fd2xp70FuM/8ngCJzj3Er1TYRLSFmp1F1OInbEm4DZH8NA==", - "dev": true, - "requires": { - "acorn": "^6.0.7", - "acorn-walk": "^6.1.1", - "bfj": "^6.1.1", - "chalk": "^2.4.1", - "commander": "^2.18.0", - "ejs": "^2.6.1", - "express": "^4.16.3", - "filesize": "^3.6.1", - "gzip-size": "^5.0.0", - "lodash": "^4.17.10", - "mkdirp": "^0.5.1", - "opener": "^1.5.1", - "ws": "^6.0.0" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - } - } - }, - "webpack-chain": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-4.12.1.tgz", - "integrity": "sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==", - "dev": true, - "requires": { - "deepmerge": "^1.5.2", - "javascript-stringify": "^1.6.0" - } - }, - "webpack-dev-middleware": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.6.1.tgz", - "integrity": "sha512-XQmemun8QJexMEvNFbD2BIg4eSKrmSIMrTfnl2nql2Sc6OGAYFyb8rwuYrCjl/IiEYYuyTEiimMscu7EXji/Dw==", - "dev": true, - "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.3.1", - "range-parser": "^1.0.3", - "webpack-log": "^2.0.0" - } - }, - "webpack-dev-server": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.2.1.tgz", - "integrity": "sha512-sjuE4mnmx6JOh9kvSbPYw3u/6uxCLHNWfhWaIPwcXWsvWOPN+nc5baq4i9jui3oOBRXGonK9+OI0jVkaz6/rCw==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.0.0", - "compression": "^1.5.2", - "connect-history-api-fallback": "^1.3.0", - "debug": "^4.1.1", - "del": "^3.0.0", - "express": "^4.16.2", - "html-entities": "^1.2.0", - "http-proxy-middleware": "^0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.2.0", - "ip": "^1.1.5", - "killable": "^1.0.0", - "loglevel": "^1.4.1", - "opn": "^5.1.0", - "portfinder": "^1.0.9", - "schema-utils": "^1.0.0", - "selfsigned": "^1.9.1", - "semver": "^5.6.0", - "serve-index": "^1.7.2", - "sockjs": "0.3.19", - "sockjs-client": "1.3.0", - "spdy": "^4.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.5.1", - "webpack-log": "^2.0.0", - "yargs": "12.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "dev": true, - "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - } - }, - "webpack-merge": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.1.tgz", - "integrity": "sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==", - "dev": true, - "requires": { - "lodash": "^4.17.5" - } - }, - "webpack-sources": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", - "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", - "dev": true, - "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "worker-farm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "optional": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, - "xregexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", - "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true - }, - "yargs": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", - "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^2.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" - } - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } - }, - "yorkie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yorkie/-/yorkie-2.0.0.tgz", - "integrity": "sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==", - "dev": true, - "requires": { - "execa": "^0.8.0", - "is-ci": "^1.0.10", - "normalize-path": "^1.0.0", - "strip-indent": "^2.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", - "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "normalize-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz", - "integrity": "sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - } - } -} diff --git a/vue/package.json b/vue/package.json deleted file mode 100644 index 67270852..00000000 --- a/vue/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "hsadmin-vue", - "version": "0.1.0", - "private": true, - "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", - "lint": "vue-cli-service lint" - }, - "dependencies": { - "axios": "^0.18.0", - "core-js": "^2.6.5", - "vue": "^2.6.6", - "vue-router": "^3.0.1" - }, - "devDependencies": { - "@vue/cli-plugin-babel": "^3.5.0", - "@vue/cli-plugin-eslint": "^3.5.0", - "@vue/cli-service": "^3.5.0", - "babel-eslint": "^10.0.1", - "eslint": "^5.8.0", - "eslint-plugin-vue": "^5.0.0", - "vue-template-compiler": "^2.5.21" - }, - "eslintConfig": { - "root": true, - "env": { - "node": true - }, - "extends": [ - "plugin:vue/essential", - "eslint:recommended" - ], - "rules": {}, - "parserOptions": { - "parser": "babel-eslint" - } - }, - "postcss": { - "plugins": { - "autoprefixer": {} - } - }, - "browserslist": [ - "> 1%", - "last 2 versions", - "not ie <= 8" - ] -} diff --git a/vue/public/favicon.ico b/vue/public/favicon.ico deleted file mode 100644 index c7b9a43c8cd16d0b434adaf513fcacb340809a11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmchVOGsN$5QZm2NTI$erQpKHrdQX(jn+pVxKN`Ng)RzW5+8_2Xb@Y)Dkd6tq9V8u z3WAh^C@KZ1kA;tohzs}b3NC_*QmUXr$oP*rH(2mdT{z*(KX=aj=bX$9kqMvFRKj;Q zwI&d~A);J>5-PDega~WT5us%#Dc(Y}C4WpP?+fS;FaZ*z_CFzgiW=w{I02=q_TUz( z?=^H2uwoIK1n%|Ay21~QgjV1emYtWttJdz^L#=DjJ@Ex*9UPc*7<=rZo*_NAh4PxA zqkso~Ioa1y$e+3kIkXi29YNLi&lW}vY6C}ut4{8ou(7w=$_=$v{yJ$h?y!&bJfq*( zL_NQRF37$6e>%9erGV?p^lRFD?|5J_eupXaS;QluyrOmBT>PJhirMYb*i?(4Tf=j~?VvnUlY_ zDCVuuk3E&T9aP~Cr-0i-MaKUjf_|U!=R&t}_CfD=d${p~HH`BPaqb9aXT}UI$iGRg z>0^GlZ`vM4?;$*LhfI(RG|XK4GF+@-W*W}YJT5&2N_ZyZuaM_Ry=%PWx>r0P(Rc?> jRc4}SfGA>*agjwN{7E7DEm(*)%rSx{B0<6wBoglxJAy|R diff --git a/vue/public/index.html b/vue/public/index.html deleted file mode 100644 index c7a0cb1a..00000000 --- a/vue/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - hsadmin-vue - - - -

- - - diff --git a/vue/src/App.vue b/vue/src/App.vue deleted file mode 100644 index d6ffee19..00000000 --- a/vue/src/App.vue +++ /dev/null @@ -1,32 +0,0 @@ - - - diff --git a/vue/src/assets/logo.png b/vue/src/assets/logo.png deleted file mode 100644 index f8748ba09fbf48e2f2f9d00fec7ec8e6e4f3c7c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7426 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 diff --git a/vue/src/components/EntityList.vue b/vue/src/components/EntityList.vue deleted file mode 100644 index 9f258473..00000000 --- a/vue/src/components/EntityList.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - diff --git a/vue/src/components/HelloWorld.vue b/vue/src/components/HelloWorld.vue deleted file mode 100644 index 879051a2..00000000 --- a/vue/src/components/HelloWorld.vue +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - diff --git a/vue/src/hsadmin.js b/vue/src/hsadmin.js deleted file mode 100644 index 37e5d6e8..00000000 --- a/vue/src/hsadmin.js +++ /dev/null @@ -1,28 +0,0 @@ -import axios from "axios" - -export default class HSAdmin { - constructor(options = {}) { - this._ax = axios.create({ - baseURL: options.baseURL || "http://localhost:45678/api", // TODO: change to production URL - }); - } - - get token() { return this._token; } - - async get(endpoint, config = {}) { - return this._ax.get(endpoint, config); - } - - async post(endpoint, data) { - return this._ax.post(endpoint, data); - } - - async authenticate(user = "admin", pass = "admin") { - const res = await this.post("authenticate", { username: user, password: pass}); - if (!res.data.id_token) { - throw new Error("authentication response is missing id_token value"); - } - this._token = res.data.id_token; - this._ax.defaults.headers.common["Authorization"] = "Bearer " + this._token; - } -} diff --git a/vue/src/main.js b/vue/src/main.js deleted file mode 100644 index 89739e29..00000000 --- a/vue/src/main.js +++ /dev/null @@ -1,10 +0,0 @@ -import Vue from 'vue' -import App from './App.vue' -import router from './router' - -Vue.config.productionTip = false; - -new Vue({ - router, - render: h => h(App) -}).$mount('#app'); diff --git a/vue/src/router.js b/vue/src/router.js deleted file mode 100644 index 70064e57..00000000 --- a/vue/src/router.js +++ /dev/null @@ -1,45 +0,0 @@ -import Vue from 'vue' -import Router from 'vue-router' -import Home from './views/Home.vue' -import HSAdmin from "./hsadmin.js" - -Vue.use(Router); - -const hsa = new HSAdmin({ - baseURL: `http://localhost:${process.env.VUE_APP_API_PORT || 8080}/api`, -}); -const routeProps = (route) => { - return Object.assign({}, route.params,{ - hsadmin: hsa - }); -}; - -export default new Router({ - mode: 'history', - base: process.env.BASE_URL, - routes: [ - { - path: '/', - name: 'home', - component: Home - }, - { - path: "/login", - name: "login", - component: () => import(/* webpackChunkName: "login" */ "./views/Login.vue"), - props: routeProps, - }, - { - path: "/customers", - name: "customers", - component: () => import(/* webpackChunkName: "customers" */ "./views/Customers.vue"), - props: routeProps, - }, - { - path: "/customers/:id", - name: "customer", - component: () => import(/* webpackChunkName: "customers" */ "./views/Customer.vue"), - props: routeProps, - }, - ] -}) diff --git a/vue/src/views/About.vue b/vue/src/views/About.vue deleted file mode 100644 index 3fa28070..00000000 --- a/vue/src/views/About.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/vue/src/views/Customer.vue b/vue/src/views/Customer.vue deleted file mode 100644 index 8bd7b534..00000000 --- a/vue/src/views/Customer.vue +++ /dev/null @@ -1,55 +0,0 @@ - - - diff --git a/vue/src/views/Customers.vue b/vue/src/views/Customers.vue deleted file mode 100644 index 9a81f6d8..00000000 --- a/vue/src/views/Customers.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/vue/src/views/Home.vue b/vue/src/views/Home.vue deleted file mode 100644 index fc2e9402..00000000 --- a/vue/src/views/Home.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/vue/src/views/Login.vue b/vue/src/views/Login.vue deleted file mode 100644 index 3d63616e..00000000 --- a/vue/src/views/Login.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/webpack/logo-jhipster.png b/webpack/logo-jhipster.png deleted file mode 100644 index d8eb48da05c157571eed9fd7303f8d9566f2ea12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4459 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 0; -} diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js deleted file mode 100644 index d1402c74..00000000 --- a/webpack/webpack.common.js +++ /dev/null @@ -1,96 +0,0 @@ -const webpack = require('webpack'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const rxPaths = require('rxjs/_esm5/path-mapping'); -const MergeJsonWebpackPlugin = require("merge-jsons-webpack-plugin"); - -const utils = require('./utils.js'); - -module.exports = (options) => ({ - resolve: { - extensions: ['.ts', '.js'], - modules: ['node_modules'], - alias: { - app: utils.root('src/main/webapp/app/'), - ...rxPaths() - } - }, - stats: { - children: false - }, - module: { - rules: [ - { - test: /\.html$/, - loader: 'html-loader', - options: { - minimize: true, - caseSensitive: true, - removeAttributeQuotes:false, - minifyJS:false, - minifyCSS:false - }, - exclude: /(src\/main\/webapp\/index.html)/ - }, - { - test: /\.(jpe?g|png|gif|svg|woff2?|ttf|eot)$/i, - loader: 'file-loader', - options: { - digest: 'hex', - hash: 'sha512', - name: 'content/[hash].[ext]' - } - }, - { - test: /manifest.webapp$/, - loader: 'file-loader', - options: { - name: 'manifest.webapp' - } - }, - // Ignore warnings about System.import in Angular - { test: /[\/\\]@angular[\/\\].+\.js$/, parser: { system: true } }, - ] - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: `'${options.env}'`, - BUILD_TIMESTAMP: `'${new Date().getTime()}'`, - VERSION: `'${utils.parseVersion()}'`, - DEBUG_INFO_ENABLED: options.env === 'development', - // The root URL for API calls, ending with a '/' - for example: `"https://www.jhipster.tech:8081/myservice/"`. - // If this URL is left empty (""), then it will be relative to the current context. - // If you use an API server, in `prod` mode, you will need to enable CORS - // (see the `jhipster.cors` common JHipster property in the `application-*.yml` configurations) - SERVER_API_URL: `''` - } - }), - new CopyWebpackPlugin([ - { from: './node_modules/swagger-ui/dist/css', to: 'swagger-ui/dist/css' }, - { from: './node_modules/swagger-ui/dist/lib', to: 'swagger-ui/dist/lib' }, - { from: './node_modules/swagger-ui/dist/swagger-ui.min.js', to: 'swagger-ui/dist/swagger-ui.min.js' }, - { from: './src/main/webapp/swagger-ui/', to: 'swagger-ui' }, - { from: './src/main/webapp/content/', to: 'content' }, - { from: './src/main/webapp/favicon.ico', to: 'favicon.ico' }, - { from: './src/main/webapp/manifest.webapp', to: 'manifest.webapp' }, - // jhipster-needle-add-assets-to-webpack - JHipster will add/remove third-party resources in this array - { from: './src/main/webapp/robots.txt', to: 'robots.txt' } - ]), - new MergeJsonWebpackPlugin({ - output: { - groupBy: [ - { pattern: "./src/main/webapp/i18n/de/*.json", fileName: "./i18n/de.json" }, - { pattern: "./src/main/webapp/i18n/en/*.json", fileName: "./i18n/en.json" } - // jhipster-needle-i18n-language-webpack - JHipster will add/remove languages in this array - ] - } - }), - new HtmlWebpackPlugin({ - template: './src/main/webapp/index.html', - chunks: ['vendors', 'polyfills', 'main', 'global'], - chunksSortMode: 'manual', - inject: 'body' - }) - ] -}); diff --git a/webpack/webpack.dev.js b/webpack/webpack.dev.js deleted file mode 100644 index 34050138..00000000 --- a/webpack/webpack.dev.js +++ /dev/null @@ -1,132 +0,0 @@ -const webpack = require('webpack'); -const writeFilePlugin = require('write-file-webpack-plugin'); -const webpackMerge = require('webpack-merge'); -const BrowserSyncPlugin = require('browser-sync-webpack-plugin'); -const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); -const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin'); -const SimpleProgressWebpackPlugin = require('simple-progress-webpack-plugin'); -const WebpackNotifierPlugin = require('webpack-notifier'); -const path = require('path'); - -const utils = require('./utils.js'); -const commonConfig = require('./webpack.common.js'); - -const ENV = 'development'; - -module.exports = (options) => webpackMerge(commonConfig({ env: ENV }), { - devtool: 'eval-source-map', - devServer: { - contentBase: './build/www', - proxy: [{ - context: [ - /* jhipster-needle-add-entity-to-webpack - JHipster will add entity api paths here */ - '/api', - '/management', - '/swagger-resources', - '/v2/api-docs', - '/h2-console', - '/auth' - ], - target: `http${options.tls ? 's' : ''}://127.0.0.1:8080`, - secure: false, - changeOrigin: options.tls, - headers: { host: 'localhost:9000' } - }], - stats: options.stats, - watchOptions: { - ignored: /node_modules/ - } - }, - entry: { - polyfills: './src/main/webapp/app/polyfills', - global: './src/main/webapp/content/css/global.css', - main: './src/main/webapp/app/app.main' - }, - output: { - path: utils.root('build/www'), - filename: 'app/[name].bundle.js', - chunkFilename: 'app/[id].chunk.js' - }, - module: { - rules: [{ - test: /\.ts$/, - enforce: 'pre', - loader: 'tslint-loader', - exclude: [/(node_modules)/, new RegExp('reflect-metadata\\' + path.sep + 'Reflect\\.ts')] - }, - { - test: /\.ts$/, - use: [ - 'angular2-template-loader', - { - loader: 'cache-loader', - options: { - cacheDirectory: path.resolve('build/cache-loader') - } - }, - { - loader: 'thread-loader', - options: { - // there should be 1 cpu for the fork-ts-checker-webpack-plugin - workers: require('os').cpus().length - 1 - } - }, - { - loader: 'ts-loader', - options: { - transpileOnly: true, - happyPackMode: true - } - }, - 'angular-router-loader' - ], - exclude: /(node_modules)/ - }, - { - test: /\.css$/, - use: ['to-string-loader', 'css-loader'], - exclude: /(vendor\.css|global\.css)/ - }, - { - test: /(vendor\.css|global\.css)/, - use: ['style-loader', 'css-loader'] - }] - }, - stats: process.env.JHI_DISABLE_WEBPACK_LOGS ? 'none' : options.stats, - plugins: [ - process.env.JHI_DISABLE_WEBPACK_LOGS - ? null - : new SimpleProgressWebpackPlugin({ - format: options.stats === 'minimal' ? 'compact' : 'expanded' - }), - new FriendlyErrorsWebpackPlugin(), - new ForkTsCheckerWebpackPlugin(), - new BrowserSyncPlugin({ - host: 'localhost', - port: 9000, - proxy: { - target: 'http://localhost:9060' - }, - socket: { - clients: { - heartbeatTimeout: 60000 - } - } - }, { - reload: false - }), - new webpack.ContextReplacementPlugin( - /angular(\\|\/)core(\\|\/)/, - path.resolve(__dirname, './src/main/webapp') - ), - new writeFilePlugin(), - new webpack.WatchIgnorePlugin([ - utils.root('src/test'), - ]), - new WebpackNotifierPlugin({ - title: 'JHipster', - contentImage: path.join(__dirname, 'logo-jhipster.png') - }) - ].filter(Boolean), - mode: 'development' -}); diff --git a/webpack/webpack.prod.js b/webpack/webpack.prod.js deleted file mode 100644 index f0be1e74..00000000 --- a/webpack/webpack.prod.js +++ /dev/null @@ -1,125 +0,0 @@ -const webpack = require('webpack'); -const webpackMerge = require('webpack-merge'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); -const Visualizer = require('webpack-visualizer-plugin'); -const MomentLocalesPlugin = require('moment-locales-webpack-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const WorkboxPlugin = require('workbox-webpack-plugin'); -const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin; -const path = require('path'); - -const utils = require('./utils.js'); -const commonConfig = require('./webpack.common.js'); - -const ENV = 'production'; - -module.exports = webpackMerge(commonConfig({ env: ENV }), { - // Enable source maps. Please note that this will slow down the build. - // You have to enable it in UglifyJSPlugin config below and in tsconfig-aot.json as well - // devtool: 'source-map', - entry: { - polyfills: './src/main/webapp/app/polyfills', - global: './src/main/webapp/content/css/global.css', - main: './src/main/webapp/app/app.main' - }, - output: { - path: utils.root('build/www'), - filename: 'app/[name].[hash].bundle.js', - chunkFilename: 'app/[id].[hash].chunk.js' - }, - module: { - rules: [{ - test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/, - loader: '@ngtools/webpack' - }, - { - test: /\.css$/, - use: ['to-string-loader', 'css-loader'], - exclude: /(vendor\.css|global\.css)/ - }, - { - test: /(vendor\.css|global\.css)/, - use: [ - MiniCssExtractPlugin.loader, - 'css-loader', - 'postcss-loader' - ] - }] - }, - optimization: { - runtimeChunk: false, - splitChunks: { - cacheGroups: { - commons: { - test: /[\\/]node_modules[\\/]/, - name: 'vendors', - chunks: 'all' - } - } - }, - minimizer: [ - new TerserPlugin({ - parallel: true, - cache: true, - terserOptions: { - ie8: false, - // sourceMap: true, // Enable source maps. Please note that this will slow down the build - compress: { - dead_code: true, - warnings: false, - properties: true, - drop_debugger: true, - conditionals: true, - booleans: true, - loops: true, - unused: true, - toplevel: true, - if_return: true, - inline: true, - join_vars: true - }, - output: { - comments: false, - beautify: false, - indent_level: 2 - } - } - }), - new OptimizeCSSAssetsPlugin({}) - ] - }, - plugins: [ - new MiniCssExtractPlugin({ - // Options similar to the same options in webpackOptions.output - // both options are optional - filename: '[name].[contenthash].css', - chunkFilename: '[id].css' - }), - new MomentLocalesPlugin({ - localesToKeep: [ - 'de', - 'en' - // jhipster-needle-i18n-language-moment-webpack - JHipster will add/remove languages in this array - ] - }), - new Visualizer({ - // Webpack statistics in target folder - filename: '../stats.html' - }), - new AngularCompilerPlugin({ - mainPath: utils.root('src/main/webapp/app/app.main.ts'), - tsConfigPath: utils.root('tsconfig-aot.json'), - sourceMap: true - }), - new webpack.LoaderOptionsPlugin({ - minimize: true, - debug: false - }), - new WorkboxPlugin.GenerateSW({ - clientsClaim: true, - skipWaiting: true, - }) - ], - mode: 'production' -}); From 377b63ca3d12b5d389c22edce7f7ecc85a0735f0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 22 Jul 2022 16:52:49 +0200 Subject: [PATCH 02/54] RbacGrants with follow=false for customer.owner to customer.admin --- sql/10-rbac-base.sql | 134 +++++++++++++++++++++---------------- sql/21-hs-customer.sql | 5 +- sql/22-hs-packages.sql | 2 +- sql/23-hs-unixuser.sql | 2 +- sql/24-hs-domain.sql | 2 +- sql/25-hs-emailaddress.sql | 2 +- sql/28-hs-tests.sql | 87 ++++++++++++++++++++++++ 7 files changed, 170 insertions(+), 64 deletions(-) create mode 100644 sql/28-hs-tests.sql diff --git a/sql/10-rbac-base.sql b/sql/10-rbac-base.sql index 1f783077..f67ec657 100644 --- a/sql/10-rbac-base.sql +++ b/sql/10-rbac-base.sql @@ -39,7 +39,7 @@ CREATE TABLE RbacGrants ( ascendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, descendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, - -- apply bool not null, -- alternative 1 to implement assumable roles + follow boolean not null default true, primary key (ascendantUuid, descendantUuid) ); CREATE INDEX ON RbacGrants (ascendantUuid); @@ -254,23 +254,19 @@ BEGIN END; $$; -CREATE OR REPLACE PROCEDURE grantRoleToRole(subRoleId uuid, superRoleId uuid - -- , doapply bool = true -- assumeV1 - ) +CREATE OR REPLACE PROCEDURE grantRoleToRole(subRoleId uuid, superRoleId uuid, doFollow bool = true ) LANGUAGE plpgsql AS $$ BEGIN perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole'); perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole'); - RAISE NOTICE 'granting subRole % to superRole %', subRoleId, superRoleId; -- TODO: remove - IF ( isGranted(subRoleId, superRoleId) ) THEN RAISE EXCEPTION 'Cyclic role grant detected between % and %', subRoleId, superRoleId; END IF; -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (superRoleId, subRoleId, doapply); -- assumeV1 - INSERT INTO RbacGrants (ascendantUuid, descendantUuid) VALUES (superRoleId, subRoleId) - ON CONFLICT DO NOTHING ; -- TODO: remove + INSERT INTO RbacGrants (ascendantUuid, descendantUuid, follow)VALUES (superRoleId, subRoleId, doFollow) + ON CONFLICT DO NOTHING ; -- TODO: remove? END; $$; CREATE OR REPLACE PROCEDURE revokeRoleFromRole(subRoleId uuid, superRoleId uuid) @@ -298,40 +294,52 @@ END; $$; abort; set local session authorization default; +CREATE OR REPLACE FUNCTION nextLevel(level integer, maxDepth integer) + RETURNS INTEGER + LANGUAGE plpgsql AS $$ + BEGIN + IF (level > maxDepth) THEN + RAISE WARNING 'Role assignment depth exceeded %/%.', level, maxDepth; + END IF; + RETURN level+1; + END; +$$; + + CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( requiredOp RbacOp, - -- objectTable varchar, -- TODO: maybe another optimization? but test perforamance for joins! + forObjectTable varchar, -- TODO: test perforamance in joins! subjectIds uuid[], - maxDepth integer = 8, maxObjects integer = 16000) RETURNS SETOF uuid RETURNS NULL ON NULL INPUT LANGUAGE plpgsql AS $$ DECLARE - foundRows bigint; + foundRows bigint; BEGIN - RETURN QUERY SELECT DISTINCT perm.objectUuid + RETURN QUERY SELECT DISTINCT perm.objectUuid FROM ( WITH RECURSIVE grants AS ( SELECT descendantUuid, ascendantUuid, 1 AS level FROM RbacGrants - WHERE ascendantUuid = ANY(subjectIds) - UNION ALL - SELECT "grant".descendantUuid, "grant".ascendantUuid, level + 1 AS level + WHERE follow AND ascendantUuid = ANY(subjectIds) + UNION DISTINCT + SELECT "grant".descendantUuid, "grant".ascendantUuid, level+1 AS level FROM RbacGrants "grant" INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid - WHERE level <= maxDepth + WHERE follow ) SELECT descendantUuid FROM grants - -- LIMIT maxObjects+1 ) as granted - JOIN RbacPermission perm ON granted.descendantUuid=perm.uuid AND perm.op IN ('*', requiredOp); + JOIN RbacPermission perm + ON granted.descendantUuid=perm.uuid AND perm.op IN ('*', requiredOp) + JOIN RbacObject obj ON obj.uuid=perm.objectUuid AND obj.objectTable=forObjectTable; foundRows = lastRowCount(); IF foundRows > maxObjects THEN RAISE EXCEPTION 'Too many accessible objects, limit is %, found %.', maxObjects, foundRows USING - ERRCODE = 'P0003', -- 'HS-ADMIN-NG:ACC-OBJ-EXC', + ERRCODE = 'P0003', HINT = 'Please assume a sub-role and try again.'; END IF; END; @@ -340,9 +348,9 @@ $$; abort; set local session authorization restricted; begin transaction; -set local statement_timeout TO '60s'; +set local statement_timeout TO '5s'; select count(*) - from queryAccessibleObjectUuidsOfSubjectIds('view', ARRAY[findRbacUser('mike@hostsharing.net')], 4, 10000); + from queryAccessibleObjectUuidsOfSubjectIds('view', 'customer', ARRAY[findRbacUser('mike@hostsharing.net')], 10000); end transaction; --- @@ -510,27 +518,20 @@ CREATE OR REPLACE FUNCTION isGranted(granteeId uuid, grantedId uuid) RETURNS bool RETURNS NULL ON NULL INPUT LANGUAGE sql AS $$ -SELECT granteeId=grantedId OR granteeId IN ( - WITH RECURSIVE grants AS ( - SELECT - descendantUuid, - ascendantUuid - FROM - RbacGrants - WHERE - descendantUuid = grantedId - UNION ALL - SELECT - "grant".descendantUuid, - "grant".ascendantUuid - FROM - RbacGrants "grant" + SELECT granteeId=grantedId OR granteeId IN ( + WITH RECURSIVE grants AS ( + SELECT descendantUuid, ascendantUuid + FROM RbacGrants + WHERE descendantUuid = grantedId + UNION ALL + SELECT "grant".descendantUuid, "grant".ascendantUuid + FROM RbacGrants "grant" INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid - ) SELECT - ascendantUuid - FROM - grants -); + ) SELECT + ascendantUuid + FROM + grants + ); $$; CREATE OR REPLACE FUNCTION isPermissionGrantedToSubject(permissionId uuid, subjectId uuid) @@ -617,17 +618,17 @@ DECLARE BEGIN BEGIN currentSubject := current_setting('hsadminng.assumedRoles'); - EXCEPTION WHEN OTHERS THEN - RETURN NULL; - END; - IF (currentSubject = '') THEN - RETURN NULL; - END IF; - RETURN string_to_array(currentSubject, ';'); -END; $$; + EXCEPTION WHEN OTHERS THEN + RETURN ARRAY[]::varchar[]; + END; + IF (currentSubject = '') THEN + RETURN ARRAY[]::varchar[]; + END IF; + RETURN string_to_array(currentSubject, ';'); + END; $$; --- ROLLBACK; +ROLLBACK; SET SESSION AUTHORIZATION DEFAULT; CREATE OR REPLACE FUNCTION currentSubjectIds() RETURNS uuid[] @@ -641,17 +642,36 @@ DECLARE BEGIN currentUserId := currentUserId(); assumedRoles := assumedRoles(); - IF ( assumedRoles IS NULL ) THEN - RETURN currentUserId; + IF ( CARDINALITY(assumedRoles) = 0 ) THEN + RETURN ARRAY[currentUserId]; END IF; RAISE NOTICE 'assuming roles: %', assumedRoles; SELECT ARRAY_AGG(uuid) FROM RbacRole WHERE name = ANY(assumedRoles) INTO assumedRoleIds; - FOREACH assumedRoleId IN ARRAY assumedRoleIds LOOP - IF ( NOT isGranted(currentUserId, assumedRoleId) ) THEN - RAISE EXCEPTION 'user % has no permission to assume role %', currentUser(), assumedRoleId; - END IF; - END LOOP; + IF assumedRoleIds IS NOT NULL THEN + FOREACH assumedRoleId IN ARRAY assumedRoleIds LOOP + IF ( NOT isGranted(currentUserId, assumedRoleId) ) THEN + RAISE EXCEPTION 'user % has no permission to assume role %', currentUser(), assumedRoleId; + END IF; + END LOOP; + END IF; RETURN assumedRoleIds; END; $$; + +rollback; +set session authorization default; +CREATE OR REPLACE FUNCTION maxGrantDepth() + RETURNS integer + STABLE LEAKPROOF + LANGUAGE plpgsql AS $$ +DECLARE + maxGrantDepth VARCHAR(63); +BEGIN + BEGIN + maxGrantDepth := current_setting('hsadminng.maxGrantDepth'); + EXCEPTION WHEN OTHERS THEN + maxGrantDepth := NULL; + END; + RETURN coalesce(maxGrantDepth, '8')::integer; +END; $$; diff --git a/sql/21-hs-customer.sql b/sql/21-hs-customer.sql index 5e050cd4..2a93ad0a 100644 --- a/sql/21-hs-customer.sql +++ b/sql/21-hs-customer.sql @@ -38,7 +38,7 @@ BEGIN -- ... also a customer admin role is created and granted to the customer owner role customerAdminRoleId = createRole('customer#'||NEW.prefix||'.admin'); - call grantRoleToRole(customerAdminRoleId, customerOwnerRoleId); + call grantRoleToRole(customerAdminRoleId, customerOwnerRoleId, false); -- ... to which a permission with view and add- ops is assigned call grantPermissionsToRole(customerAdminRoleId, createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view', 'add-package'])); @@ -88,12 +88,11 @@ CREATE TRIGGER deleteRbacRulesForCustomer_Trigger SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE customer ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS cust_view; DROP VIEW IF EXISTS customer_rv; CREATE OR REPLACE VIEW customer_rv AS SELECT DISTINCT target.* FROM customer AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'customer', currentSubjectIds()) AS allowedObjId ON target.uuid = allowedObjId; GRANT ALL PRIVILEGES ON customer_rv TO restricted; diff --git a/sql/22-hs-packages.sql b/sql/22-hs-packages.sql index 7322e3b1..4f0e76d4 100644 --- a/sql/22-hs-packages.sql +++ b/sql/22-hs-packages.sql @@ -78,7 +78,7 @@ DROP VIEW IF EXISTS package_rv; CREATE OR REPLACE VIEW package_rv AS SELECT DISTINCT target.* FROM package AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'package', currentSubjectIds()) AS allowedObjId ON target.uuid = allowedObjId; GRANT ALL PRIVILEGES ON package_rv TO restricted; diff --git a/sql/23-hs-unixuser.sql b/sql/23-hs-unixuser.sql index 77ed62b6..91b92c98 100644 --- a/sql/23-hs-unixuser.sql +++ b/sql/23-hs-unixuser.sql @@ -71,7 +71,7 @@ DROP VIEW IF EXISTS unixuser_rv; CREATE OR REPLACE VIEW unixuser_rv AS SELECT DISTINCT target.* FROM unixuser AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'unixuser', currentSubjectIds()) AS allowedObjId ON target.uuid = allowedObjId; GRANT ALL PRIVILEGES ON unixuser_rv TO restricted; diff --git a/sql/24-hs-domain.sql b/sql/24-hs-domain.sql index f19e1f97..dc4afdef 100644 --- a/sql/24-hs-domain.sql +++ b/sql/24-hs-domain.sql @@ -56,7 +56,7 @@ DROP VIEW IF EXISTS domain_rv; CREATE OR REPLACE VIEW domain_rv AS SELECT DISTINCT target.* FROM Domain AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'domain', currentSubjectIds()) AS allowedObjId ON target.uuid = allowedObjId; GRANT ALL PRIVILEGES ON domain_rv TO restricted; diff --git a/sql/25-hs-emailaddress.sql b/sql/25-hs-emailaddress.sql index 6956fded..7a37d9c2 100644 --- a/sql/25-hs-emailaddress.sql +++ b/sql/25-hs-emailaddress.sql @@ -81,7 +81,7 @@ DROP VIEW IF EXISTS EMailAddress_rv; CREATE OR REPLACE VIEW EMailAddress_rv AS SELECT DISTINCT target.* FROM EMailAddress AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId + JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'emailaddress', currentSubjectIds()) AS allowedObjId ON target.uuid = allowedObjId; GRANT ALL PRIVILEGES ON EMailAddress_rv TO restricted; diff --git a/sql/28-hs-tests.sql b/sql/28-hs-tests.sql new file mode 100644 index 00000000..029ba24f --- /dev/null +++ b/sql/28-hs-tests.sql @@ -0,0 +1,87 @@ + + +-- hostmaster listing all customers +ROLLBACK; +BEGIN TRANSACTION; +SET SESSION SESSION AUTHORIZATION restricted; +SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; +SET LOCAL hsadminng.assumedRoles = ''; +SELECT * FROM customer_rv; +END TRANSACTION; + +-- customer admin listing all their packages +ROLLBACK; +BEGIN TRANSACTION; +SET SESSION SESSION AUTHORIZATION restricted; +SET LOCAL hsadminng.currentUser = 'admin@aae.example.com'; +SET LOCAL hsadminng.assumedRoles = ''; +SELECT * FROM package_rv; +END TRANSACTION; + + +-- cutomer admin listing all their unix users +ROLLBACK; +BEGIN TRANSACTION; +SET SESSION SESSION AUTHORIZATION restricted; +SET LOCAL hsadminng.currentUser = 'admin@aae.example.com'; +SET LOCAL hsadminng.assumedRoles = ''; + +SELECT * FROM unixuser_rv; +END TRANSACTION; + + +-- hostsharing admin assuming customer role and listing all accessible packages +ROLLBACK; +BEGIN TRANSACTION; +SET SESSION SESSION AUTHORIZATION restricted; +SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; +SET LOCAL hsadminng.assumedRoles = 'customer#bbb.admin;customer#bbc.admin'; +SELECT * FROM package_rv p; +END TRANSACTION; + +-- hostsharing admin assuming two customer admin role and listing all accessible unixusers +ROLLBACK; +BEGIN TRANSACTION; +SET SESSION SESSION AUTHORIZATION restricted; +SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; +SET LOCAL hsadminng.assumedRoles = 'customer#bbb.admin;customer#bbc.admin'; + +SELECT c.prefix, c.reference, uu.* +FROM unixuser_rv uu + JOIN package_rv p ON p.uuid = uu.packageuuid + JOIN customer_rv c ON c.uuid = p.customeruuid; +END TRANSACTION; + +BEGIN TRANSACTION; +SET SESSION SESSION AUTHORIZATION restricted; +SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; +SET LOCAL hsadminng.assumedRoles = 'customer#bbb.admin;customer#bbc.admin'; + +SELECT p.name, uu.name, dom.name +FROM domain_rv dom + JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid + JOIN package_rv p ON p.uuid = uu.packageuuid + JOIN customer_rv c ON c.uuid = p.customeruuid; +END TRANSACTION; + +BEGIN TRANSACTION; +SET SESSION SESSION AUTHORIZATION restricted; +SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; +SET LOCAL hsadminng.assumedRoles = 'customer#bbb.admin;customer#bbc.admin'; +-- TODO: we need tenant roles on parent objects +-- SET LOCAL hsadminng.assumedRoles = 'package#bbb03.owner;package#bbb08.owner'; + +SELECT p.name as "package", ema.localPart || '@' || dom.name as "email-address" +FROM emailaddress_rv ema + JOIN domain_rv dom ON dom.uuid = ema.domainuuid + JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid + JOIN package_rv p ON p.uuid = uu.packageuuid + JOIN customer_rv c ON c.uuid = p.customeruuid; +END TRANSACTION; + +ROLLBACK; +BEGIN TRANSACTION; +SET SESSION SESSION AUTHORIZATION restricted; +SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; +select * from customer_rv c where c.prefix='bbb'; +END TRANSACTION; From d4eeb35e91e7a16707e89603db6aa91bdd021c34 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 25 Jul 2022 16:38:21 +0200 Subject: [PATCH 03/54] improved role structure including comprised tenant sub roles --- .run/00-util.sql.run.xml | 8 + .run/10-rbac-base.sql.run.xml | 8 + .run/12-rbac-role-builder.sql.run.xml | 8 + .run/18--rbac-all.sql.run.xml | 8 + .run/20-hs-base.sql.run.xml | 8 + .run/21-hs-customer.sql.run.xml | 8 + .run/22-hs-packages.sql.run.xml | 8 + .run/23-hs-unixuser.sql.run.xml | 8 + .run/24-hs-domain.sql.run.xml | 8 + .run/25-hs-emailaddress.sql.run.xml | 8 + .run/29-hs-statistics.sql.run.xml | 8 + .run/30-run-all.sql.run.xml | 8 + .run/run all up to emailaddresses.run.xml | 18 ++ sql/00-util.sql | 13 +- sql/10-rbac-base.sql | 49 +++-- ...bac-view.sql => 11--rbac-view-options.sql} | 0 sql/12-rbac-role-builder.sql | 208 ++++++++++++++++++ ...-statistics.sql => 18-rbac-statistics.sql} | 2 +- sql/{19-rbac-tests.sql => 19--rbac-tests.sql} | 0 sql/21-hs-customer.sql | 72 +++--- sql/22-hs-packages.sql | 60 +++-- sql/23-hs-unixuser.sql | 76 +++++-- sql/24-hs-domain.sql | 59 ++++- sql/25-hs-emailaddress.sql | 70 +++--- sql/{28-hs-tests.sql => 28--hs-tests.sql} | 32 +-- sql/29-hs-statistics.sql | 4 +- 26 files changed, 613 insertions(+), 146 deletions(-) create mode 100644 .run/00-util.sql.run.xml create mode 100644 .run/10-rbac-base.sql.run.xml create mode 100644 .run/12-rbac-role-builder.sql.run.xml create mode 100644 .run/18--rbac-all.sql.run.xml create mode 100644 .run/20-hs-base.sql.run.xml create mode 100644 .run/21-hs-customer.sql.run.xml create mode 100644 .run/22-hs-packages.sql.run.xml create mode 100644 .run/23-hs-unixuser.sql.run.xml create mode 100644 .run/24-hs-domain.sql.run.xml create mode 100644 .run/25-hs-emailaddress.sql.run.xml create mode 100644 .run/29-hs-statistics.sql.run.xml create mode 100644 .run/30-run-all.sql.run.xml create mode 100644 .run/run all up to emailaddresses.run.xml rename sql/{11-rbac-view.sql => 11--rbac-view-options.sql} (100%) create mode 100644 sql/12-rbac-role-builder.sql rename sql/{rbac-statistics.sql => 18-rbac-statistics.sql} (95%) rename sql/{19-rbac-tests.sql => 19--rbac-tests.sql} (100%) rename sql/{28-hs-tests.sql => 28--hs-tests.sql} (72%) diff --git a/.run/00-util.sql.run.xml b/.run/00-util.sql.run.xml new file mode 100644 index 00000000..765612b0 --- /dev/null +++ b/.run/00-util.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/10-rbac-base.sql.run.xml b/.run/10-rbac-base.sql.run.xml new file mode 100644 index 00000000..161d087d --- /dev/null +++ b/.run/10-rbac-base.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/12-rbac-role-builder.sql.run.xml b/.run/12-rbac-role-builder.sql.run.xml new file mode 100644 index 00000000..795a8278 --- /dev/null +++ b/.run/12-rbac-role-builder.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/18--rbac-all.sql.run.xml b/.run/18--rbac-all.sql.run.xml new file mode 100644 index 00000000..fb6890e6 --- /dev/null +++ b/.run/18--rbac-all.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/20-hs-base.sql.run.xml b/.run/20-hs-base.sql.run.xml new file mode 100644 index 00000000..90368bd3 --- /dev/null +++ b/.run/20-hs-base.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/21-hs-customer.sql.run.xml b/.run/21-hs-customer.sql.run.xml new file mode 100644 index 00000000..0b9f91e8 --- /dev/null +++ b/.run/21-hs-customer.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/22-hs-packages.sql.run.xml b/.run/22-hs-packages.sql.run.xml new file mode 100644 index 00000000..84095cc5 --- /dev/null +++ b/.run/22-hs-packages.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/23-hs-unixuser.sql.run.xml b/.run/23-hs-unixuser.sql.run.xml new file mode 100644 index 00000000..7b87185b --- /dev/null +++ b/.run/23-hs-unixuser.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/24-hs-domain.sql.run.xml b/.run/24-hs-domain.sql.run.xml new file mode 100644 index 00000000..678151b0 --- /dev/null +++ b/.run/24-hs-domain.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/25-hs-emailaddress.sql.run.xml b/.run/25-hs-emailaddress.sql.run.xml new file mode 100644 index 00000000..e33ea706 --- /dev/null +++ b/.run/25-hs-emailaddress.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/29-hs-statistics.sql.run.xml b/.run/29-hs-statistics.sql.run.xml new file mode 100644 index 00000000..e9790ad4 --- /dev/null +++ b/.run/29-hs-statistics.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/30-run-all.sql.run.xml b/.run/30-run-all.sql.run.xml new file mode 100644 index 00000000..e17e8564 --- /dev/null +++ b/.run/30-run-all.sql.run.xml @@ -0,0 +1,8 @@ + + + + FILE + + + + \ No newline at end of file diff --git a/.run/run all up to emailaddresses.run.xml b/.run/run all up to emailaddresses.run.xml new file mode 100644 index 00000000..ddbed0d5 --- /dev/null +++ b/.run/run all up to emailaddresses.run.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + FILE + + + + \ No newline at end of file diff --git a/sql/00-util.sql b/sql/00-util.sql index 79326344..06755f11 100644 --- a/sql/00-util.sql +++ b/sql/00-util.sql @@ -35,4 +35,15 @@ ELSE RETURN partial; END IF; END; $$; -SELECT * FROM intToVarChar(211, 4); + +select * from intToVarChar(211, 4); + +CREATE OR REPLACE FUNCTION randomInRange(min INTEGER, max INTEGER) + RETURNS INT + RETURNS NULL ON NULL INPUT + language 'plpgsql' AS $$ +BEGIN + RETURN floor(random() * (max-min + 1) + min); +END; $$; + +select * from randomInRange(0, 4); diff --git a/sql/10-rbac-base.sql b/sql/10-rbac-base.sql index f67ec657..2219c00c 100644 --- a/sql/10-rbac-base.sql +++ b/sql/10-rbac-base.sql @@ -45,7 +45,7 @@ CREATE TABLE RbacGrants CREATE INDEX ON RbacGrants (ascendantUuid); CREATE INDEX ON RbacGrants (descendantUuid); -DROP DOMAIN IF EXISTS RbacOp CASCADE; +-- DROP DOMAIN IF EXISTS RbacOp CASCADE; CREATE DOMAIN RbacOp AS VARCHAR(67) CHECK( VALUE = '*' @@ -56,7 +56,7 @@ CREATE DOMAIN RbacOp AS VARCHAR(67) OR VALUE ~ '^add-[a-z]+$' ); -DROP TABLE IF EXISTS RbacObject; +-- DROP TABLE IF EXISTS RbacObject; CREATE TABLE RbacObject ( uuid uuid UNIQUE DEFAULT uuid_generate_v4(), @@ -80,7 +80,7 @@ BEGIN END; $$; -DROP TABLE IF EXISTS RbacPermission; +-- DROP TABLE IF EXISTS RbacPermission; CREATE TABLE RbacPermission ( uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, objectUuid uuid not null, @@ -111,13 +111,15 @@ BEGIN END; $$; -CREATE OR REPLACE FUNCTION findRbacUser(userName varchar) -- TODO: rename to ...Id +CREATE OR REPLACE FUNCTION findRbacUserId(userName varchar) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE sql AS $$ SELECT uuid FROM RbacUser WHERE name = userName $$; +CREATE TYPE RbacWhenNotExists AS ENUM ('fail', 'create'); + CREATE OR REPLACE FUNCTION getRbacUserId(userName varchar, whenNotExists RbacWhenNotExists) RETURNS uuid RETURNS NULL ON NULL INPUT @@ -125,7 +127,7 @@ CREATE OR REPLACE FUNCTION getRbacUserId(userName varchar, whenNotExists RbacWhe DECLARE userUuid uuid; BEGIN - userUuid = findRbacUser(userName); + userUuid = findRbacUserId(userName); IF ( userUuid IS NULL ) THEN IF ( whenNotExists = 'fail') THEN RAISE EXCEPTION 'RbacUser with name="%" not found', userName; @@ -169,8 +171,6 @@ CREATE OR REPLACE FUNCTION findRoleId(roleName varchar) SELECT uuid FROM RbacRole WHERE name = roleName $$; -CREATE TYPE RbacWhenNotExists AS ENUM ('fail', 'create'); - CREATE OR REPLACE FUNCTION getRoleId(roleName varchar, whenNotExists RbacWhenNotExists) RETURNS uuid RETURNS NULL ON NULL INPUT @@ -265,7 +265,7 @@ BEGIN END IF; -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (superRoleId, subRoleId, doapply); -- assumeV1 - INSERT INTO RbacGrants (ascendantUuid, descendantUuid, follow)VALUES (superRoleId, subRoleId, doFollow) + INSERT INTO RbacGrants (ascendantUuid, descendantUuid, follow) VALUES (superRoleId, subRoleId, doFollow) ON CONFLICT DO NOTHING ; -- TODO: remove? END; $$; @@ -288,7 +288,7 @@ BEGIN -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (userId, roleId, true); -- assumeV1 INSERT INTO RbacGrants (ascendantUuid, descendantUuid) VALUES (userId, roleId) - ON CONFLICT DO NOTHING ; -- TODO: remove + ON CONFLICT DO NOTHING ; -- TODO: remove? END; $$; abort; @@ -306,9 +306,12 @@ CREATE OR REPLACE FUNCTION nextLevel(level integer, maxDepth integer) $$; +abort; +set local session authorization default; + CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( requiredOp RbacOp, - forObjectTable varchar, -- TODO: test perforamance in joins! + forObjectTable varchar, -- TODO: test performance in joins! subjectIds uuid[], maxObjects integer = 16000) RETURNS SETOF uuid @@ -327,7 +330,7 @@ CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( SELECT "grant".descendantUuid, "grant".ascendantUuid, level+1 AS level FROM RbacGrants "grant" INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid - WHERE follow + WHERE follow ) SELECT descendantUuid FROM grants ) as granted @@ -345,12 +348,18 @@ CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( END; $$; +SET SESSION AUTHORIZATION DEFAULT; +CREATE ROLE admin; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin; +CREATE ROLE restricted; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; + abort; set local session authorization restricted; begin transaction; -set local statement_timeout TO '5s'; +set local statement_timeout TO '15s'; select count(*) - from queryAccessibleObjectUuidsOfSubjectIds('view', 'customer', ARRAY[findRbacUser('mike@hostsharing.net')], 10000); + from queryAccessibleObjectUuidsOfSubjectIds('view', 'customer', ARRAY[findRbacUserId('mike@hostsharing.net')], 10000); end transaction; --- @@ -389,7 +398,7 @@ set local session authorization restricted; begin transaction; -- set local statement_timeout TO '5s'; set local statement_timeout TO '5min'; -select count(*) from queryRequiredPermissionsOfSubjectIds('view', ARRAY[findRbacUser('mike@hostsharing.net')]); +select count(*) from queryRequiredPermissionsOfSubjectIds('view', ARRAY[findRbacUserId('mike@hostsharing.net')]); end transaction; --- @@ -424,7 +433,7 @@ abort; set local session authorization restricted; begin transaction; set local statement_timeout TO '5s'; - select count(*) from queryAllPermissionsOfSubjectIds(ARRAY[findRbacUser('mike@hostsharing.net')]); + select count(*) from queryAllPermissionsOfSubjectIds(ARRAY[findRbacUserId('mike@hostsharing.net')]); end transaction; --- @@ -564,18 +573,10 @@ CREATE OR REPLACE FUNCTION isPermissionGrantedToSubject(permissionId uuid, subje ); $$; -SET SESSION AUTHORIZATION DEFAULT; -CREATE ROLE admin; -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin; -CREATE ROLE restricted; -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; - - -- ======================================================== --- Current User +-- current user + assumed roles -- -------------------------------------------------------- - CREATE OR REPLACE FUNCTION currentUser() RETURNS varchar(63) STABLE LEAKPROOF diff --git a/sql/11-rbac-view.sql b/sql/11--rbac-view-options.sql similarity index 100% rename from sql/11-rbac-view.sql rename to sql/11--rbac-view-options.sql diff --git a/sql/12-rbac-role-builder.sql b/sql/12-rbac-role-builder.sql new file mode 100644 index 00000000..fc24de10 --- /dev/null +++ b/sql/12-rbac-role-builder.sql @@ -0,0 +1,208 @@ + + +-- ======================================================== +-- Role-Hierarcy helper functions +-- -------------------------------------------------------- + +CREATE TYPE RbacRoleType AS ENUM ('owner', 'admin', 'tenant'); + +-- PERMISSIONS -------------------------------------------- + +-- drop type RbacPermissions; +CREATE TYPE RbacPermissions AS +( + permissionUuids uuid[] +); + +CREATE OR REPLACE FUNCTION grantingPermissions(forObjectUuid uuid, permitOps RbacOp[]) + RETURNS RbacPermissions + LANGUAGE plpgsql STRICT AS $$ +BEGIN + RETURN ROW(createPermissions(forObjectUuid, permitOps))::RbacPermissions; +END; $$; + +-- SUPER ROLES -------------------------------------------- + +-- drop type RbacSuperRoles; +CREATE TYPE RbacSuperRoles AS +( + roleUuids uuid[] +); + +-- drop function beneathRoles(roleName varchar); +CREATE OR REPLACE FUNCTION beneathRoles(roleNames varchar[]) + RETURNS RbacSuperRoles + LANGUAGE plpgsql STRICT AS $$ +DECLARE + superRoleName varchar; + superRoleUuids uuid[] := ARRAY[]::uuid[]; +BEGIN + FOREACH superRoleName IN ARRAY roleNames LOOP + superRoleUuids := superRoleUuids || getRoleId(superRoleName, 'fail'); + END LOOP; + + RETURN ROW(superRoleUuids)::RbacSuperRoles; +END; $$; + +-- drop function beneathRole(roleName varchar); +CREATE OR REPLACE FUNCTION beneathRole(roleName varchar) + RETURNS RbacSuperRoles + LANGUAGE plpgsql STRICT AS $$ +BEGIN + RETURN beneathRoles(ARRAY[roleName]); +END; $$; + +-- drop function beneathRole(roleUuid uuid); +CREATE OR REPLACE FUNCTION beneathRole(roleUuid uuid) + RETURNS RbacSuperRoles + LANGUAGE plpgsql STRICT AS $$ +BEGIN + RETURN ROW(ARRAY[roleUuid]::uuid[])::RbacSuperRoles; +END; $$; + +-- drop function asTopLevelRole(roleName varchar); +CREATE OR REPLACE FUNCTION asTopLevelRole() + RETURNS RbacSuperRoles + LANGUAGE plpgsql STRICT AS $$ +BEGIN + RETURN ROW(ARRAY[]::uuid[])::RbacSuperRoles; +END; $$; + +-- SUB ROLES ---------------------------------------------- + +CREATE TYPE RbacSubRoles AS +( + roleUuids uuid[] +); + +-- drop FUNCTION beingItselfA(roleUuid uuid) +CREATE OR REPLACE FUNCTION beingItselfA(roleUuid uuid) + RETURNS RbacSubRoles + LANGUAGE plpgsql STRICT AS $$ +BEGIN + RETURN ROW(ARRAY[roleUuid]::uuid[])::RbacSubRoles; +END; $$; + +-- drop FUNCTION beingItselfA(roleName varchar) +CREATE OR REPLACE FUNCTION beingItselfA(roleName varchar) + RETURNS RbacSubRoles + LANGUAGE plpgsql STRICT AS $$ +BEGIN + RETURN beingItselfA(getRoleId(roleName, 'fail')); +END; $$; + +-- USERS -------------------------------------------------- + +-- drop type RbacUsers; +CREATE TYPE RbacUsers AS +( + userUuids uuid[] +); + +-- drop function withUsers(userNames varchar); +CREATE OR REPLACE FUNCTION withUsers(userNames varchar[]) + RETURNS RbacUsers + LANGUAGE plpgsql STRICT AS $$ +DECLARE + userName varchar; + userUuids uuid[] := ARRAY[]::uuid[]; +BEGIN + FOREACH userName IN ARRAY userNames LOOP + userUuids := userUuids || getRbacUserId(userName, 'fail'); + END LOOP; + + RETURN ROW(userUuids)::RbacUsers; +END; $$; + + +-- DROP FUNCTION withUser(userName varchar, whenNotExists RbacWhenNotExists); +CREATE OR REPLACE FUNCTION withUser(userName varchar, whenNotExists RbacWhenNotExists = 'fail') + RETURNS RbacUsers + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +BEGIN + RETURN ROW(ARRAY[getRbacUserId(userName, whenNotExists )]); +END; $$; + +-- ROLE NAME BUILDER -------------------------------------- + +CREATE OR REPLACE FUNCTION roleName(objectTable varchar, objectName varchar, roleType RbacRoleType ) + RETURNS varchar + RETURNS NULL ON NULL INPUT + STABLE LEAKPROOF + LANGUAGE plpgsql AS $$ +BEGIN + RETURN objectTable || '#' || objectName || '.' || roleType; +END; $$; + + +-- CREATE ROLE MAIN FUNCTION ------------------------------ + +CREATE OR REPLACE FUNCTION createRole( + roleName varchar, + permissions RbacPermissions, + superRoles RbacSuperRoles, + subRoles RbacSubRoles = null, + users RbacUsers = null +) + RETURNS uuid + CALLED ON NULL INPUT + LANGUAGE plpgsql AS $$ +DECLARE + roleUuid uuid; + superRoleUuid uuid; + subRoleUuid uuid; + userUuid uuid; +BEGIN + RAISE NOTICE 'creating role: %', roleName; + roleUuid = createRole(roleName); + + call grantPermissionsToRole(roleUuid, permissions.permissionUuids); + + IF superRoles IS NOT NULL THEN + FOREACH superRoleUuid IN ARRAY superRoles.roleuUids LOOP + call grantRoleToRole(roleUuid, superRoleUuid); + END LOOP; + END IF; + + IF subRoles IS NOT NULL THEN + FOREACH subRoleUuid IN ARRAY subRoles.roleuUids LOOP + call grantRoleToRole(subRoleUuid, roleUuid); + END LOOP; + END IF; + + IF users IS NOT NULL THEN + FOREACH userUuid IN ARRAY users.useruUids LOOP + call grantRoleToUser(roleUuid, userUuid); + END LOOP; + END IF; + + RETURN roleUuid; +END; $$; + +CREATE OR REPLACE FUNCTION createRole( + roleName varchar, + permissions RbacPermissions, + users RbacUsers = null +) + RETURNS uuid + CALLED ON NULL INPUT + LANGUAGE plpgsql AS $$ +BEGIN + RETURN createRole(roleName, permissions, null, null, users); +END; $$; + +CREATE OR REPLACE FUNCTION createRole( + roleName varchar, + permissions RbacPermissions, + subRoles RbacSubRoles, + users RbacUsers = null +) + RETURNS uuid + CALLED ON NULL INPUT + LANGUAGE plpgsql AS $$ +BEGIN + RETURN createRole(roleName, permissions, null, subRoles, users); +END; $$; + + diff --git a/sql/rbac-statistics.sql b/sql/18-rbac-statistics.sql similarity index 95% rename from sql/rbac-statistics.sql rename to sql/18-rbac-statistics.sql index 3430bcb4..7bb5dac7 100644 --- a/sql/rbac-statistics.sql +++ b/sql/18-rbac-statistics.sql @@ -1,5 +1,5 @@ -DROP VIEW "RbacStatisticsV"; +DROP VIEW IF EXISTS "RbacStatisticsV"; CREATE VIEW "RbacStatisticsV" AS SELECT no, to_char("count", '9 999 999 999') as "count", "table" FROM ( diff --git a/sql/19-rbac-tests.sql b/sql/19--rbac-tests.sql similarity index 100% rename from sql/19-rbac-tests.sql rename to sql/19--rbac-tests.sql diff --git a/sql/21-hs-customer.sql b/sql/21-hs-customer.sql index 2a93ad0a..99a0a091 100644 --- a/sql/21-hs-customer.sql +++ b/sql/21-hs-customer.sql @@ -17,40 +17,62 @@ CREATE TRIGGER createRbacObjectForCustomer_Trigger BEFORE INSERT ON customer FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); +CREATE OR REPLACE FUNCTION customerOwner(customerName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('customer', customerName, 'owner'); +end; $$; + +CREATE OR REPLACE FUNCTION customerAdmin(customerName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('customer', customerName, 'admin'); +end; $$; + +CREATE OR REPLACE FUNCTION customerTenant(customerName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('customer', customerName, 'tenant'); +end; $$; + + CREATE OR REPLACE FUNCTION createRbacRulesForCustomer() RETURNS trigger LANGUAGE plpgsql STRICT AS $$ DECLARE - adminUserNameUuid uuid; - customerOwnerRoleId uuid; - customerAdminRoleId uuid; + customerOwnerUuid uuid; + customerAdminUuid uuid; BEGIN IF TG_OP <> 'INSERT' THEN RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; END IF; - -- an owner role is created and assigned to the administrators group - customerOwnerRoleId = createRole('customer#'||NEW.prefix||'.owner'); - call grantRoleToRole(customerOwnerRoleId, getRoleId('administrators', 'create')); - -- ... and permissions for all ops are assigned - call grantPermissionsToRole(customerOwnerRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + -- the owner role with full access for Hostsharing administrators + customerOwnerUuid = createRole( + customerOwner(NEW.prefix), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + beneathRole('administrators') + ); - -- ... also a customer admin role is created and granted to the customer owner role - customerAdminRoleId = createRole('customer#'||NEW.prefix||'.admin'); - call grantRoleToRole(customerAdminRoleId, customerOwnerRoleId, false); - -- ... to which a permission with view and add- ops is assigned - call grantPermissionsToRole(customerAdminRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view', 'add-package'])); - -- if a admin user is given for the customer, - IF (NEW.adminUserName IS NOT NULL) THEN - -- ... the customer admin role is also assigned to the admin user of the customer - adminUserNameUuid = findRoleId(NEW.adminUserName); - IF ( adminUserNameUuid IS NULL ) THEN - adminUserNameUuid = createRbacUser(NEW.adminUserName); - END IF; - call grantRoleToUser(customerAdminRoleId, adminUserNameUuid); - END IF; + -- the admin role for the customer's admins, who can view and add products + customerAdminUuid = createRole( + customerAdmin(NEW.prefix), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view', 'add-package']), + -- NO auto follow for customer owner to avoid exploding permissions for administrators + withUser(NEW.adminUserName, 'create') -- implicitly ignored if null + ); + + -- allow the customer owner role (thus administrators) to assume the customer admin role + call grantRoleToRole(customerAdminUuid, customerOwnerUuid, FALSE); + + -- the tenant role which later can be used by owners+admins of sub-objects + perform createRole( + customerTenant(NEW.prefix), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view']) + ); RETURN NEW; END; $$; @@ -109,7 +131,7 @@ DO LANGUAGE plpgsql $$ BEGIN SET hsadminng.currentUser TO ''; - FOR t IN 0..9999 LOOP + FOR t IN 0..69 LOOP currentTask = 'creating RBAC test customer #' || t; SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net'; SET LOCAL hsadminng.assumedRoles = ''; diff --git a/sql/22-hs-packages.sql b/sql/22-hs-packages.sql index 4f0e76d4..676f9b1f 100644 --- a/sql/22-hs-packages.sql +++ b/sql/22-hs-packages.sql @@ -11,6 +11,28 @@ CREATE TABLE IF NOT EXISTS package ( customerUuid uuid REFERENCES customer(uuid) ); +CREATE OR REPLACE FUNCTION packageOwner(packageName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('package', packageName, 'owner'); +end; $$; + +CREATE OR REPLACE FUNCTION packageAdmin(packageName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('package', packageName, 'admin'); +end; $$; + +CREATE OR REPLACE FUNCTION packageTenant(packageName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('package', packageName, 'tenant'); +end; $$; + + DROP TRIGGER IF EXISTS createRbacObjectForPackage_Trigger ON package; CREATE TRIGGER createRbacObjectForPackage_Trigger BEFORE INSERT ON package @@ -21,8 +43,8 @@ CREATE OR REPLACE FUNCTION createRbacRulesForPackage() LANGUAGE plpgsql STRICT AS $$ DECLARE parentCustomer customer; - packageOwnerRoleId uuid; - packageTenantRoleId uuid; + packageOwnerRoleUuid uuid; + packageAdminRoleUuid uuid; BEGIN IF TG_OP <> 'INSERT' THEN RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; @@ -30,21 +52,27 @@ BEGIN SELECT * FROM customer AS c WHERE c.uuid=NEW.customerUuid INTO parentCustomer; - -- an owner role is created and assigned to the customer's admin group - packageOwnerRoleId = createRole('package#'||NEW.name||'.owner'); - call grantRoleToRole(packageOwnerRoleId, getRoleId('customer#'||parentCustomer.prefix||'.admin', 'fail')); + -- an owner role is created and assigned to the customer's admin role + packageOwnerRoleUuid = createRole( + packageOwner(NEW.name), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + beneathRole(customerAdmin(parentCustomer.prefix)) + ); - -- ... and permissions for all ops are assigned - call grantPermissionsToRole(packageOwnerRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + -- an owner role is created and assigned to the package owner role + packageAdminRoleUuid = createRole( + packageAdmin(NEW.name), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-unixuser']), + beneathRole(packageOwnerRoleUuid) + ); - -- ... also a package tenant role is created and assigned to the package owner as well - packageTenantRoleId = createRole('package#'||NEW.name||'.tenant'); - call grantRoleToRole(packageTenantRoleId, packageOwnerRoleId); - - -- ... to which a permission with view operation is assigned - call grantPermissionsToRole(packageTenantRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view'])); + -- and a package tenant role is created and assigned to the package admin as well + perform createRole( + packageTenant(NEW.name), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY ['view']), + beneathRole(packageAdminRoleUuid), + beingItselfA(customerTenant(parentCustomer.prefix)) + ); RETURN NEW; END; $$; @@ -96,7 +124,7 @@ DO LANGUAGE plpgsql $$ SET hsadminng.currentUser TO ''; FOR cust IN (SELECT * FROM customer) LOOP - FOR t IN 0..9 LOOP + FOR t IN 0..randominrange(1, 2) LOOP pacName = cust.prefix || TO_CHAR(t, 'fm00'); currentTask = 'creating RBAC test package #'|| pacName || ' for customer ' || cust.prefix || ' #' || cust.uuid; RAISE NOTICE 'task: %', currentTask; diff --git a/sql/23-hs-unixuser.sql b/sql/23-hs-unixuser.sql index 91b92c98..dcc992a6 100644 --- a/sql/23-hs-unixuser.sql +++ b/sql/23-hs-unixuser.sql @@ -11,6 +11,49 @@ CREATE TABLE IF NOT EXISTS UnixUser ( packageUuid uuid REFERENCES package(uuid) ); +CREATE OR REPLACE FUNCTION unixUserOwner(unixUserName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('unixuser', unixUserName, 'owner'); +end; $$; + +CREATE OR REPLACE FUNCTION unixUserAdmin(unixUserName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('unixuser', unixUserName, 'admin'); +end; $$; + +CREATE OR REPLACE FUNCTION unixUserTenant(unixUserName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('unixuser', unixUserName, 'tenant'); +end; $$; + +CREATE OR REPLACE FUNCTION createUnixUserTenantRoleIfNotExists(unixUser UnixUser) + RETURNS uuid + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +DECLARE + unixUserTenantRoleName varchar; + unixUserTenantRoleUuid uuid; +BEGIN + unixUserTenantRoleName = unixUserTenant(unixUser.name); + unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleName); + IF unixUserTenantRoleUuid IS NOT NULL THEN + RETURN unixUserTenantRoleUuid; + END IF; + + RETURN createRole( + unixUserTenantRoleName, + grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => ARRAY['edit', 'add-domain']), + beneathRole(unixUserAdmin(unixUser.name)) + ); +END; $$; + + DROP TRIGGER IF EXISTS createRbacObjectForUnixUser_Trigger ON UnixUser; CREATE TRIGGER createRbacObjectForUnixUser_Trigger BEFORE INSERT ON UnixUser @@ -23,7 +66,6 @@ DECLARE parentPackage package; unixuserOwnerRoleId uuid; unixuserAdminRoleId uuid; - unixuserTenantRoleId uuid; BEGIN IF TG_OP <> 'INSERT' THEN RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; @@ -31,26 +73,22 @@ BEGIN SELECT * FROM package WHERE uuid=NEW.packageUuid into parentPackage; - -- an owner role is created and assigned to the package owner group - unixuserOwnerRoleId = createRole('unixuser#'||NEW.name||'.owner'); - call grantRoleToRole(unixuserOwnerRoleId, getRoleId('package#'||parentPackage.name||'.owner', 'fail')); - -- ... and permissions for all ops are assigned - call grantPermissionsToRole(unixuserOwnerRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + -- an owner role is created and assigned to the package's admin group + unixuserOwnerRoleId = createRole( + unixUserOwner(NEW.name), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + beneathRole(packageAdmin(parentPackage.name)) + ); - -- ... also a unixuser admin role is created and assigned to the unixuser owner as well - unixuserAdminRoleId = createRole('unixuser#'||NEW.name||'.admin'); - call grantRoleToRole(unixuserAdminRoleId, unixuserOwnerRoleId); - -- ... to which a permission with view operation is assigned - call grantPermissionsToRole(unixuserAdminRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-domain'])); + -- and a unixuser admin role is created and assigned to the unixuser owner as well + unixuserAdminRoleId = createRole( + unixUserAdmin(NEW.name), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-domain']), + beneathRole(unixuserOwnerRoleId), + beingItselfA(packageTenant(parentPackage.name)) + ); - -- ... also a unixuser tenant role is created and assigned to the unixuser admin - unixuserTenantRoleId = createRole('unixuser#'||NEW.name||'.tenant'); - call grantRoleToRole(unixuserTenantRoleId, unixuserAdminRoleId); - -- ... to which a permission with view operation is assigned - call grantPermissionsToRole(unixuserTenantRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view'])); + -- a tenent role is only created on demand RETURN NEW; END; $$; diff --git a/sql/24-hs-domain.sql b/sql/24-hs-domain.sql index dc4afdef..ce730f40 100644 --- a/sql/24-hs-domain.sql +++ b/sql/24-hs-domain.sql @@ -16,12 +16,35 @@ CREATE TRIGGER createRbacObjectForDomain_Trigger BEFORE INSERT ON Domain FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); +CREATE OR REPLACE FUNCTION domainOwner(unixUserName varchar, domainName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('domain', unixUserName || '/' || domainName, 'owner'); +end; $$; + +CREATE OR REPLACE FUNCTION domainAdmin(unixUserName varchar, domainName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('domain', unixUserName || '/' || domainName, 'admin'); +end; $$; + +CREATE OR REPLACE FUNCTION domainTenant(unixUserName varchar, domainName varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('domain', unixUserName || '/' || domainName, 'tenant'); +end; $$; + + CREATE OR REPLACE FUNCTION createRbacRulesForDomain() RETURNS trigger LANGUAGE plpgsql STRICT AS $$ DECLARE - parentUser unixuser; - domainOwnerRoleId uuid; + parentUser unixuser; + domainOwnerRoleUuid uuid; + domainAdminRoleUuid uuid; BEGIN IF TG_OP <> 'INSERT' THEN RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; @@ -29,13 +52,27 @@ BEGIN SELECT * FROM unixuser WHERE uuid=NEW.unixUserUuid into parentUser; - -- an owner role is created and assigned to the unix user admin - RAISE NOTICE 'creating domain owner role: %', 'domain#'||NEW.name||'.owner'; - domainOwnerRoleId = getRoleId('domain#'||NEW.name||'.owner', 'create'); - call grantRoleToRole(domainOwnerRoleId, getRoleId('unixuser#'||parentUser.name||'.admin', 'fail')); - -- ... and permissions for all ops are assigned - call grantPermissionsToRole(domainOwnerRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + -- a domain owner role is created and assigned to the unixuser's admin role + domainOwnerRoleUuid = createRole( + domainOwner(parentUser.name, NEW.name), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + beneathRole(unixUserAdmin(parentUser.name)) + ); + + -- a domain admin role is created and assigned to the domain's owner role + domainAdminRoleUuid = createRole( + domainAdmin(parentUser.name, NEW.name), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-emailaddress']), + beneathRole(domainOwnerRoleUuid) + ); + + -- and a domain tenant role is created and assigned to the domain's admiin role + perform createRole( + domainTenant(parentUser.name, NEW.name), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + beneathRole(domainAdminRoleUuid), + beingItselfA(createUnixUserTenantRoleIfNotExists(parentUser)) + ); RETURN NEW; END; $$; @@ -74,7 +111,7 @@ DO LANGUAGE plpgsql $$ FOR uu IN (SELECT * FROM unixuser) LOOP IF ( random() < 0.3 ) THEN - FOR t IN 0..2 LOOP + FOR t IN 0..1 LOOP currentTask = 'creating RBAC test Domain #' || t || ' for UnixUser ' || uu.name|| ' #' || uu.uuid; RAISE NOTICE 'task: %', currentTask; @@ -85,7 +122,7 @@ DO LANGUAGE plpgsql $$ SET LOCAL hsadminng.currentTask TO currentTask; INSERT INTO Domain (name, unixUserUuid) - VALUES ('dom-' || t || '.' || pac.name || '.example.org' , uu.uuid); + VALUES ('dom-' || t || '.' || uu.name || '.example.org' , uu.uuid); COMMIT; END LOOP; diff --git a/sql/25-hs-emailaddress.sql b/sql/25-hs-emailaddress.sql index 7a37d9c2..49c25d06 100644 --- a/sql/25-hs-emailaddress.sql +++ b/sql/25-hs-emailaddress.sql @@ -16,35 +16,51 @@ CREATE TRIGGER createRbacObjectForEMailAddress_Trigger BEFORE INSERT ON EMailAddress FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); +CREATE OR REPLACE FUNCTION emailAddressOwner(emailAddress varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('emailaddress', emailAddress, 'owner'); +end; $$; + +CREATE OR REPLACE FUNCTION emailAddressAdmin(emailAddress varchar) + RETURNS varchar + LANGUAGE plpgsql STRICT AS $$ +begin + return roleName('emailaddress', emailAddress, 'admin'); +end; $$; + CREATE OR REPLACE FUNCTION createRbacRulesForEMailAddress() RETURNS trigger LANGUAGE plpgsql STRICT AS $$ DECLARE - eMailAddress varchar; - parentDomain domain; - eMailAddressOwnerRoleId uuid; - eMailAddressTenantRoleId uuid; + parentDomain record; + eMailAddress varchar; + eMailAddressOwnerRoleUuid uuid; BEGIN IF TG_OP <> 'INSERT' THEN RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; END IF; - SELECT * FROM domain WHERE uuid=NEW.domainUuid into parentDomain; + SELECT d.name as name, u.name as unixUserName FROM domain d + LEFT JOIN unixuser u ON u.uuid = d.unixuseruuid + WHERE d.uuid=NEW.domainUuid into parentDomain; eMailAddress = NEW.localPart || '@' || parentDomain.name; - -- an owner role is created and assigned to the domain owner - eMailAddressOwnerRoleId = getRoleId('emailaddress#'||eMailAddress||'.owner', 'create'); - call grantRoleToRole(eMailAddressOwnerRoleId, getRoleId('domain#'||parentDomain.name||'.owner', 'fail')); - -- ... and permissions for all ops are assigned - call grantPermissionsToRole(eMailAddressOwnerRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); + -- an owner role is created and assigned to the domains's admin group + eMailAddressOwnerRoleUuid = createRole( + emailAddressOwner(eMailAddress), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + beneathRole(domainAdmin( parentDomain.unixUserName, parentDomain.name)) + ); - -- a tenant role is created and assigned to a user with the new email address - eMailAddressTenantRoleId = getRoleId('emailaddress#'||eMailAddress||'.tenant', 'create'); - call grantRoleToUser(eMailAddressTenantRoleId, getRbacUserId(eMailAddress, 'create')); - -- ... and permissions for all ops are assigned - call grantPermissionsToRole(eMailAddressTenantRoleId, - createPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*'])); -- TODO '*' -> 'edit', 'view' + -- and an admin role is created and assigned to the unixuser owner as well + perform createRole( + emailAddressAdmin(eMailAddress), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit']), + beneathRole(eMailAddressOwnerRoleUuid), + beingItselfA(domainTenant(parentDomain.unixUserName, parentDomain.name)) + ); RETURN NEW; END; $$; @@ -59,22 +75,6 @@ CREATE TRIGGER createRbacRulesForEMailAddress_Trigger -- create RBAC restricted view -abort; -set session authorization default ; -START TRANSACTION; - SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; - SET LOCAL hsadminng.assumedRoles = 'customer#bbb.owner;customer#bbc.owner'; - -- SET LOCAL hsadminng.assumedRoles = 'package#bbb00.owner;package#bbb01.owner'; - - select count(*) from queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds(), 7) as a - join rbacobject as o on a=o.uuid; - - /* SELECT DISTINCT target.* - FROM EMailAddress AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', currentSubjectIds()) AS allowedObjId - ON target.uuid = allowedObjId;*/ -END TRANSACTION; - SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS EMailAddress_rv; @@ -99,7 +99,7 @@ DO LANGUAGE plpgsql $$ SET hsadminng.currentUser TO ''; FOR dom IN (SELECT * FROM domain) LOOP - FOR t IN 0..5 LOOP + FOR t IN 0..4 LOOP currentTask = 'creating RBAC test EMailAddress #' || t || ' for Domain ' || dom.name; RAISE NOTICE 'task: %', currentTask; @@ -111,7 +111,7 @@ DO LANGUAGE plpgsql $$ SET LOCAL hsadminng.currentTask TO currentTask; INSERT INTO EMailAddress (localPart, domainUuid) - VALUES ('local' || t, dom.uuid); + VALUES ('local' || t, dom.uuid); COMMIT; END LOOP; diff --git a/sql/28-hs-tests.sql b/sql/28--hs-tests.sql similarity index 72% rename from sql/28-hs-tests.sql rename to sql/28--hs-tests.sql index 029ba24f..46c72c86 100644 --- a/sql/28-hs-tests.sql +++ b/sql/28--hs-tests.sql @@ -35,16 +35,18 @@ ROLLBACK; BEGIN TRANSACTION; SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -SET LOCAL hsadminng.assumedRoles = 'customer#bbb.admin;customer#bbc.admin'; +SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; SELECT * FROM package_rv p; END TRANSACTION; +--- + -- hostsharing admin assuming two customer admin role and listing all accessible unixusers ROLLBACK; BEGIN TRANSACTION; SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -SET LOCAL hsadminng.assumedRoles = 'customer#bbb.admin;customer#bbc.admin'; +SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; SELECT c.prefix, c.reference, uu.* FROM unixuser_rv uu @@ -52,10 +54,12 @@ FROM unixuser_rv uu JOIN customer_rv c ON c.uuid = p.customeruuid; END TRANSACTION; +--- + BEGIN TRANSACTION; SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -SET LOCAL hsadminng.assumedRoles = 'customer#bbb.admin;customer#bbc.admin'; +SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; SELECT p.name, uu.name, dom.name FROM domain_rv dom @@ -64,24 +68,26 @@ FROM domain_rv dom JOIN customer_rv c ON c.uuid = p.customeruuid; END TRANSACTION; +--- + BEGIN TRANSACTION; SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -SET LOCAL hsadminng.assumedRoles = 'customer#bbb.admin;customer#bbc.admin'; --- TODO: we need tenant roles on parent objects --- SET LOCAL hsadminng.assumedRoles = 'package#bbb03.owner;package#bbb08.owner'; +SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; -SELECT p.name as "package", ema.localPart || '@' || dom.name as "email-address" -FROM emailaddress_rv ema - JOIN domain_rv dom ON dom.uuid = ema.domainuuid - JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid - JOIN package_rv p ON p.uuid = uu.packageuuid - JOIN customer_rv c ON c.uuid = p.customeruuid; +SELECT c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address" + FROM emailaddress_rv ema + JOIN domain_rv dom ON dom.uuid = ema.domainuuid + JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid + JOIN package_rv p ON p.uuid = uu.packageuuid + JOIN customer_rv c ON c.uuid = p.customeruuid; END TRANSACTION; +--- + ROLLBACK; BEGIN TRANSACTION; SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -select * from customer_rv c where c.prefix='bbb'; +select * from customer_rv c where c.prefix='aab'; END TRANSACTION; diff --git a/sql/29-hs-statistics.sql b/sql/29-hs-statistics.sql index 7dbb3f83..d296a2d9 100644 --- a/sql/29-hs-statistics.sql +++ b/sql/29-hs-statistics.sql @@ -5,7 +5,7 @@ DROP VIEW IF EXISTS "BusinessTableStatisticsV"; CREATE VIEW "BusinessTableStatisticsV" AS -SELECT no, to_char("count", '999 999 999') as "count", to_char("required", '999 999 999') as "required", to_char("count"::float/"required"::float, '990.9') as "factor", "table" +SELECT no, to_char("count", '999 999 999') as "count", to_char("required", '999 999 999') as "required", to_char("count"::float/"required"::float, '990.999') as "factor", "table" FROM (select 1 as no, count(*) as "count", 7000 as "required", 'customers' as "table" from customer UNION @@ -22,3 +22,5 @@ FROM (select 1 as no, count(*) as "count", 7000 as "required", 'customers' as " from emailaddress ) totals ORDER BY totals.no; + +SELECT * FROM "BusinessTableStatisticsV"; From 16513f07865f2233db238d23aede6bade7b15db3 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 25 Jul 2022 17:06:42 +0200 Subject: [PATCH 04/54] remove generated pdf --- sql/rbac.pdf | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 sql/rbac.pdf diff --git a/sql/rbac.pdf b/sql/rbac.pdf deleted file mode 100644 index e69de29b..00000000 From 4814e7899ce95ad4152bc973e1aa78f088f69efd Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 25 Jul 2022 17:15:01 +0200 Subject: [PATCH 05/54] better layout for RBAC entity diagram --- sql/rbac.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sql/rbac.md b/sql/rbac.md index 4e4c1c5e..40d79fa5 100644 --- a/sql/rbac.md +++ b/sql/rbac.md @@ -30,18 +30,17 @@ package RBAC { ' forward declarations entity RbacUser - entity RbacObject together { entity RbacRole entity RbacPermission - enum RbacOperation + RbacUser -[hidden]> RbacRole RbacRole -[hidden]> RbacUser } - + together { entity RbacGrant enum RbacReferenceType @@ -54,8 +53,8 @@ package RBAC { descendantUuid: uuid(RbackReference) auto } - RbacGrant o-> RbacReference - RbacGrant o-> RbacReference + RbacGrant o-u-> RbacReference + RbacGrant o-u-> RbacReference enum RbacReferenceType { RbacUser @@ -85,6 +84,11 @@ package RBAC { name : varchar } RbacRole o-- RbacReference + + together { + enum RbacOperation + entity RbacObject + } entity RbacPermission { *uuid : uuid(RbacReference) From 04a343818279fb8b15e057d48c66d59496f87777 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 25 Jul 2022 20:11:39 +0200 Subject: [PATCH 06/54] improve documentation --- sql/21-hs-customer.sql | 2 +- sql/rbac.md | 282 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 256 insertions(+), 28 deletions(-) diff --git a/sql/21-hs-customer.sql b/sql/21-hs-customer.sql index 99a0a091..3d861d19 100644 --- a/sql/21-hs-customer.sql +++ b/sql/21-hs-customer.sql @@ -131,7 +131,7 @@ DO LANGUAGE plpgsql $$ BEGIN SET hsadminng.currentUser TO ''; - FOR t IN 0..69 LOOP + FOR t IN 0..6999 LOOP currentTask = 'creating RBAC test customer #' || t; SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net'; SET LOCAL hsadminng.assumedRoles = ''; diff --git a/sql/rbac.md b/sql/rbac.md index 40d79fa5..24062679 100644 --- a/sql/rbac.md +++ b/sql/rbac.md @@ -141,6 +141,11 @@ An *RbacReference* is a generalization of all entity types which participate in The primary key of the *RbacReference* and its referred object is always identical. +#### RbacReferenceType + +The enum *RbacReferenceType* describes the type of reference. +It's only needed to make it easier to find the referred object in *RbacUser*, *RbacRole* or *RbacPermission*. + #### RbacUser An *RbacUser* is a type of RBAC-subject which references a login account outside this system, identified by a name (usually an email-address). @@ -167,16 +172,17 @@ An *RbacPermission* allows a specific *RbacOperation* on a specific *RbacObject* An *RbacOperation* determines, what an *RbacPermission* allows to do. It can be one of: -- **add-...** - permits creating new instances of specific entity types underneath the object specified by the permission, e.g. "add-package" -- **view** - permits reading the contents of the object specified by the permission -- **edit** - change the contents of the object specified by the permission -- **delete** - delete the object specified by the permission +- **'add-...'** - permits creating new instances of specific entity types underneath the object specified by the permission, e.g. "add-package" +- **'view'** - permits reading the contents of the object specified by the permission +- **'edit'** - change the contents of the object specified by the permission +- **'delete'** - delete the object specified by the permission +- **'\*'** This list is extensible according to the needs of the access rule system. -Please notice, that there is no **create-...** operation to create new instances of related business-object-types. +Please notice, that there is no **create** operation to create new instances of unrelated business-object-types. For such a singleton business-object-type, e.g. *Organization" or "Hostsharing" has to be defined, and its single entity is referred in the permission. -By this, the foreign key in *RbacPermission* can be defined as `NOT NULL`. +Only with this rule, the foreign key in *RbacPermission* can be defined as `NOT NULL`. #### RbacGrant @@ -186,17 +192,51 @@ The core SQL queries to determine access rights are all recursive queries on the ### Role naming -Automatically generated roles are named as such: +The naming pattern of a role is important to be able to address specific roles. +E.g. if a new package is added, the admin-role of the related customer has to be addressed. -#### business-table#business-object-name.tenant -This role is assigned to users who manage objects underneath the object which is accessible through the role. -This rule usually gets only view permissions assigned. +There can be global roles like 'administrators'. +Most roles, though, are specific for certain business-objects and automatically generated as such: -**Example** + business-object-table#business-object-name.relative-role -'dd' -## Example Users, Roles, Permissions and Business-Objects +Where *business-object-table* is the name of the SQL table of the business object (e.g *customer* or 'package'), +*business-object-name* is generated from an immutable business key(e.g. a prefix like 'xyz' or 'xyz00') +and the *relative-role*' describes the role relative to the referenced business-object as follows: + +#### owner + +The owner-role is granted to the subject which created the business object. +E.g. for a new *customer* it would be granted to 'administrators' and for a new *package* to the 'customer#...admin'. + +Whoever has the owner-role assigned can do everything with the related business-object, including deleting (or deactivating) it. + +In most cases, the permissions to other operations than 'delete' are granted through the 'admin' role. +By this, all roles ob sub-objects, which are assigned to the 'admin' role, are also granted to the 'owner'. + +#### admin + +The admin-role is granted to a role of those subjects who manage the business object. +E.g. a 'package' is manged by the admin of the customer. + +Whoever has the admin-role assigned, do everything with the related business-object, including deleting (or deactivating) it. + +In most cases, the permissions to the 'view' operation is granted through the 'tenant' role. +By this, all roles ob sub-objects, which are assigned to the 'tenent' role, are also granted to the 'admin'. + + +#### tenant + +The tenant-role is granted to everybody who needs to be able to view the business-object. +Usually all owners, admins and tenants of sub-objects get this role granted. + +Some business-objects only have very limited data directly in the main business-object and store more sensitive data in special sub-objects (e.g. 'customer-details') to which tenants of sub-objects of the main-object (e.g. package admins) do not get view permission. + + +## Example Users, Roles, Permissions and Business-Objects + +The following diagram shows how users, roles and permissions could be granted access to operations on business objects. ```plantuml @startuml @@ -270,6 +310,66 @@ PermPackXyz00_AddUser o--> PackXyz00 @enduml ``` +## Business-Object-Tables, Triggers and Views + +To support the RBAC system, for each business-object-table, some more artifacts are created in the database: + +- a `BEFORE INSERT TRIGGER` which creates the related *RbacObject* instance, +- an `AFTER INSERT TRIGGER` which creates the related *RbacRole*s, *RbacPermission*s together with their related *RbacReference*s as well as *RbacGrant*s, +- a restricted view (e.g. *customer_rv*) through which restricted users can access the underlying data. + +Not yet implemented, but planned are these actions: + +- an `ON DELETE ... DO INSTEAD` rule to allow `SQL DELETE` if applicable for the business-object-table and the user has 'delete' permission, +- an `ON UPDATE ... DO INSTEAD` rule to allow `SQL UPDATE` if the user has 'edit' right, +- an `ON INSERT ... DO INSTEAD` rule to allow `SQL INSERT` if the user has 'add-..' right to the parent-business-object. + +The restricted view takes the current user from a session property and applies the hierarchy of its roles all the way down to the permissions related to the respective business-object-table. +This way, each user can only view the data they have 'view'-permission for, only create those they have 'add-...'-permission, only update those they have 'edit'- and only delete those they have 'delete'-permission to. + +### Current User + +The current use is taken from the session variable `hsadminng.currentUser` which contains the name of the user as stored in the +*RbacUser*s table. Example: + + SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; + +That user is also used for historicization and audit log, but which is a different topic. + +### Assuming Roles + +If the session variable `hsadminng.assumedRoles` is set to a non-empty value, its content is interpreted as a list of semicolon-separated role names. +Example: + + SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; + +In this case, not the current user but the assumed roles are used as a starting point for any further queries. +Roles which are not granted to the current user, directly or indirectly, cannot be assumed. + + +### Example + +A full example is shown here: + + BEGIN TRANSACTION; + SET SESSION SESSION AUTHORIZATION restricted; + SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; + SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; + + SELECT c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address" + FROM emailaddress_rv ema + JOIN domain_rv dom ON dom.uuid = ema.domainuuid + JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid + JOIN package_rv p ON p.uuid = uu.packageuuid + JOIN customer_rv c ON c.uuid = p.customeruuid; + END TRANSACTION; + + + +## Roles and Their Assignments for Certain Business Objects + +To give you an overview of the business-object-types for the following role-examples, +check this diagram: ```plantuml @startuml @@ -282,24 +382,152 @@ hide circle ' use right-angled line routing ' skinparam linetype ortho -package rbacPerms { - cust -} +entity EMailAddress -package rbacRoles { - entity administrators - entity custXXX -} +entity Domain +Domain o-- "*" EMailAddress -package rbacUsers { - entity adminMike - adminMike <-- administrators +entity UnixUser +UnixUser o-- "*" Domain - entity adminSven - entity custXXX - entity pacAdmXXX00 -} +entity Package +Package o.. "*" UnixUser + +entity Customer +Customer o-- "*" Package @enduml ``` +It's mostly an example hierarchy of business-object-types, but resembles a part of Hostsharing's actual hosting infrastructure. + +The following diagrams show which roles are created for each business-object-type +and how they relate to roles from other business-object-types. + +### Customer Roles + +The highest level of the business-object-type-hierarchy is the *Customer*. + +```plantuml +@startuml +' left to right direction +top to bottom direction + +' hide the ugly E in a circle left to the entity name +hide circle + +' use right-angled line routing +' skinparam linetype ortho + +entity "BObj customer#xyz" as boCustXyz + +together { + entity "Perm customer#xyz *" as permCustomerXyzAll + permCustomerXyzAll --> boCustXyz + + entity "Perm customer#xyz add-package" as permCustomerXyzAddPack + permCustomerXyzAddPack --> boCustXyz + + entity "Perm customer#xyz view" as permCustomerXyzView + permCustomerXyzView --> boCustXyz +} + +entity "Role customer#xyz.tenant" as roleCustXyzTenant +roleCustXyzTenant --> permCustomerXyzView + +entity "Role customer#xyz.admin" as roleCustXyzAdmin +roleCustXyzAdmin --> roleCustXyzTenant +roleCustXyzAdmin --> permCustomerXyzAddPack + +entity "Role customer#xyz.owner" as roleCustXyzOwner +roleCustXyzOwner ..> roleCustXyzAdmin +roleCustXyzOwner --> permCustomerXyzAll + +entity "Role administrators" as roleAdmins +roleAdmins --> roleCustXyzOwner + +@enduml +``` + +As you can see, there something special: +From the 'Role customer#xyz.owner' to the 'Role customer#xyz.admin' there is a dashed line, whereas all other lines are solid lines. +Solid lines means, that one role is granted to another and followed in all queries to the restricted views. +The dashed line means that one role is granted to another but not automatically followed in queries to the restricted views. + +The reason here is that otherwise simply too many objects would be accessible to those with the 'administrators' role and all queries would be slowed down vastly. + +Grants which are not followed are still valid grants for `hsadminng.assumedRoles`. +Thus, if you want to access anything below a customer, assume its role first. + +There is actually another speciality in the customer roles: +For all others, a user defined by the customer gets the owner role assigned, just for the customer, the owners role is assigned to the 'administrators'. + + +### Package Roles + +One example of the business-object-type-level right below is the *Package*. + +```plantuml +@startuml +' left to right direction +top to bottom direction + +' hide the ugly E in a circle left to the entity name +hide circle + +' use right-angled line routing +' skinparam linetype ortho + +entity "BObj pacage#xyz00" as boPacXyz00 + +together { + entity "Perm pacage#xyz00 *" as permPackageXyzAll + permPackageXyzAll --> boPacXyz00 + + entity "Perm pacage#xyz00 add-unixuser" as permPacXyz00AddUser + permPacXyz00AddUser --> boPacXyz00 + + entity "Perm pacage#xyz00 edit" as permPacXyz00Edit + permPacXyz00Edit --> boPacXyz00 + + entity "Perm pacage#xyz00 view" as permPacXyz00View + permPacXyz00View --> boPacXyz00 +} + +package { + entity "Role customer#xyz.tenant" as roleCustXyzTenant + entity "Role customer#xyz.admin" as roleCustXyzAdmin + entity "Role customer#xyz.owner" as roleCustXyzOwner +} + +package { + entity "Role pacage#xyz00.owner" as rolePacXyz00Owner + entity "Role pacage#xyz00.admin" as rolePacXyz00Admin + entity "Role pacage#xyz00.tenant" as rolePacXyz00Tenant +} + +rolePacXyz00Tenant --> permPacXyz00View +rolePacXyz00Tenant --> roleCustXyzTenant + +rolePacXyz00Owner --> rolePacXyz00Admin +rolePacXyz00Owner --> permPackageXyzAll + +roleCustXyzAdmin --> rolePacXyz00Owner +roleCustXyzAdmin --> roleCustXyzTenant + +roleCustXyzOwner ..> roleCustXyzAdmin + +rolePacXyz00Admin --> rolePacXyz00Tenant +rolePacXyz00Admin --> permPacXyz00AddUser +rolePacXyz00Admin --> permPacXyz00Edit + +entity "Role administrators" as roleAdmins +roleAdmins --> roleCustXyzOwner + +@enduml +``` + +Initially, the customer's admin role gets the package owner role granted. +They can use the package's admin role to hand over most management functionality to a third party. +The 'administrators' can get access through an assumed customer's admin role or directly by assuming the package's owner or admin role. + From 46c5f5e53eb09644a5eb6b5205314ad0bab8c08b Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 26 Jul 2022 13:18:03 +0200 Subject: [PATCH 07/54] final performance tests as do block to measure total time --- sql/28--hs-tests.sql | 169 ++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 91 deletions(-) diff --git a/sql/28--hs-tests.sql b/sql/28--hs-tests.sql index 46c72c86..ae1e73e7 100644 --- a/sql/28--hs-tests.sql +++ b/sql/28--hs-tests.sql @@ -1,93 +1,80 @@ +DO LANGUAGE plpgsql $$ +BEGIN + + -- hostmaster accessing a single customer + SET SESSION SESSION AUTHORIZATION restricted; + SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; + PERFORM * from customer_rv c where c.prefix='aab'; + + -- hostmaster listing all customers + SET SESSION SESSION AUTHORIZATION restricted; + SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; + SET LOCAL hsadminng.assumedRoles = ''; + PERFORM * FROM customer_rv; + + -- customer admin listing all their packages + SET SESSION SESSION AUTHORIZATION restricted; + SET LOCAL hsadminng.currentUser = 'admin@aae.example.com'; + SET LOCAL hsadminng.assumedRoles = ''; + PERFORM * FROM package_rv; + + -- cutomer admin listing all their unix users + SET SESSION SESSION AUTHORIZATION restricted; + SET LOCAL hsadminng.currentUser = 'admin@aae.example.com'; + SET LOCAL hsadminng.assumedRoles = ''; + PERFORM * FROM unixuser_rv; + + -- hostsharing admin assuming customer role and listing all accessible packages + SET SESSION SESSION AUTHORIZATION restricted; + SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; + SET LOCAL hsadminng.assumedRoles = 'customer#aaa.admin;customer#aab.admin'; + PERFORM * FROM package_rv p; + + -- hostsharing admin assuming two customer admin roles and listing all accessible unixusers + SET SESSION SESSION AUTHORIZATION restricted; + SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; + SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; + PERFORM c.prefix, c.reference, uu.* + FROM unixuser_rv uu + JOIN package_rv p ON p.uuid = uu.packageuuid + JOIN customer_rv c ON c.uuid = p.customeruuid; + + -- hostsharing admin assuming two customer admin roles and listing all accessible domains + SET SESSION SESSION AUTHORIZATION restricted; + SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; + SET LOCAL hsadminng.assumedRoles = 'customer#aac.admin;customer#aad.admin'; + PERFORM p.name, uu.name, dom.name + FROM domain_rv dom + JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid + JOIN package_rv p ON p.uuid = uu.packageuuid + JOIN customer_rv c ON c.uuid = p.customeruuid; + + -- hostsharing admin assuming two customer admin roles and listing all accessible email addresses + SET SESSION SESSION AUTHORIZATION restricted; + SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; + SET LOCAL hsadminng.assumedRoles = 'customer#aad.admin;customer#aae.admin'; + PERFORM c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address" + FROM emailaddress_rv ema + JOIN domain_rv dom ON dom.uuid = ema.domainuuid + JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid + JOIN package_rv p ON p.uuid = uu.packageuuid + JOIN customer_rv c ON c.uuid = p.customeruuid; +END; $$; + +/* +=== with 7000 customers === + +1. [2022-07-26 09:17:19] completed in 801 ms +2. [2022-07-26 09:17:32] completed in 649 ms +3. [2022-07-26 09:17:51] completed in 670 ms + +no count required factor table +1 7 000 7 000 1.000 customers +2 17 436 15 000 1.162 packages +3 174 360 150 000 1.162 unixuser +4 105 206 100 000 1.052 domain +5 526 030 500 000 1.052 emailaddress + --- hostmaster listing all customers -ROLLBACK; -BEGIN TRANSACTION; -SET SESSION SESSION AUTHORIZATION restricted; -SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -SET LOCAL hsadminng.assumedRoles = ''; -SELECT * FROM customer_rv; -END TRANSACTION; - --- customer admin listing all their packages -ROLLBACK; -BEGIN TRANSACTION; -SET SESSION SESSION AUTHORIZATION restricted; -SET LOCAL hsadminng.currentUser = 'admin@aae.example.com'; -SET LOCAL hsadminng.assumedRoles = ''; -SELECT * FROM package_rv; -END TRANSACTION; - - --- cutomer admin listing all their unix users -ROLLBACK; -BEGIN TRANSACTION; -SET SESSION SESSION AUTHORIZATION restricted; -SET LOCAL hsadminng.currentUser = 'admin@aae.example.com'; -SET LOCAL hsadminng.assumedRoles = ''; - -SELECT * FROM unixuser_rv; -END TRANSACTION; - - --- hostsharing admin assuming customer role and listing all accessible packages -ROLLBACK; -BEGIN TRANSACTION; -SET SESSION SESSION AUTHORIZATION restricted; -SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; -SELECT * FROM package_rv p; -END TRANSACTION; - ---- - --- hostsharing admin assuming two customer admin role and listing all accessible unixusers -ROLLBACK; -BEGIN TRANSACTION; -SET SESSION SESSION AUTHORIZATION restricted; -SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; - -SELECT c.prefix, c.reference, uu.* -FROM unixuser_rv uu - JOIN package_rv p ON p.uuid = uu.packageuuid - JOIN customer_rv c ON c.uuid = p.customeruuid; -END TRANSACTION; - ---- - -BEGIN TRANSACTION; -SET SESSION SESSION AUTHORIZATION restricted; -SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; - -SELECT p.name, uu.name, dom.name -FROM domain_rv dom - JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid - JOIN package_rv p ON p.uuid = uu.packageuuid - JOIN customer_rv c ON c.uuid = p.customeruuid; -END TRANSACTION; - ---- - -BEGIN TRANSACTION; -SET SESSION SESSION AUTHORIZATION restricted; -SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; - -SELECT c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address" - FROM emailaddress_rv ema - JOIN domain_rv dom ON dom.uuid = ema.domainuuid - JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid - JOIN package_rv p ON p.uuid = uu.packageuuid - JOIN customer_rv c ON c.uuid = p.customeruuid; -END TRANSACTION; - ---- - -ROLLBACK; -BEGIN TRANSACTION; -SET SESSION SESSION AUTHORIZATION restricted; -SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -select * from customer_rv c where c.prefix='aab'; -END TRANSACTION; + */ From 190d39400ad0a8db348d074c122c3b854fae18dc Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 26 Jul 2022 13:18:36 +0200 Subject: [PATCH 08/54] add users to customer+package role diagrams --- sql/rbac.md | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/sql/rbac.md b/sql/rbac.md index 24062679..a7abd39d 100644 --- a/sql/rbac.md +++ b/sql/rbac.md @@ -419,6 +419,9 @@ hide circle ' use right-angled line routing ' skinparam linetype ortho +' needs PlantUML 1.2021.14 as Markdown plugin +allow_mixing + entity "BObj customer#xyz" as boCustXyz together { @@ -443,9 +446,16 @@ entity "Role customer#xyz.owner" as roleCustXyzOwner roleCustXyzOwner ..> roleCustXyzAdmin roleCustXyzOwner --> permCustomerXyzAll +actor "Customer XYZ Admin" as actorCustXyzAdmin +actorCustXyzAdmin --> roleCustXyzAdmin + entity "Role administrators" as roleAdmins roleAdmins --> roleCustXyzOwner +actor "Any Hostmaster" as actorHostmaster +actorHostmaster --> roleAdmins + + @enduml ``` @@ -460,7 +470,7 @@ Grants which are not followed are still valid grants for `hsadminng.assumedRoles Thus, if you want to access anything below a customer, assume its role first. There is actually another speciality in the customer roles: -For all others, a user defined by the customer gets the owner role assigned, just for the customer, the owners role is assigned to the 'administrators'. +For all others, a user defined by the customer gets the owner role assigned, just for the customer, the owner's role is assigned to the 'administrators' role. ### Package Roles @@ -478,19 +488,22 @@ hide circle ' use right-angled line routing ' skinparam linetype ortho -entity "BObj pacage#xyz00" as boPacXyz00 +' needs PlantUML 1.2021.14 as Markdown plugin +allow_mixing + +entity "BObj package#xyz00" as boPacXyz00 together { - entity "Perm pacage#xyz00 *" as permPackageXyzAll + entity "Perm package#xyz00 *" as permPackageXyzAll permPackageXyzAll --> boPacXyz00 - entity "Perm pacage#xyz00 add-unixuser" as permPacXyz00AddUser + entity "Perm package#xyz00 add-unixuser" as permPacXyz00AddUser permPacXyz00AddUser --> boPacXyz00 - entity "Perm pacage#xyz00 edit" as permPacXyz00Edit + entity "Perm package#xyz00 edit" as permPacXyz00Edit permPacXyz00Edit --> boPacXyz00 - entity "Perm pacage#xyz00 view" as permPacXyz00View + entity "Perm package#xyz00 view" as permPacXyz00View permPacXyz00View --> boPacXyz00 } @@ -501,9 +514,9 @@ package { } package { - entity "Role pacage#xyz00.owner" as rolePacXyz00Owner - entity "Role pacage#xyz00.admin" as rolePacXyz00Admin - entity "Role pacage#xyz00.tenant" as rolePacXyz00Tenant + entity "Role package#xyz00.owner" as rolePacXyz00Owner + entity "Role package#xyz00.admin" as rolePacXyz00Admin + entity "Role package#xyz00.tenant" as rolePacXyz00Tenant } rolePacXyz00Tenant --> permPacXyz00View @@ -521,13 +534,22 @@ rolePacXyz00Admin --> rolePacXyz00Tenant rolePacXyz00Admin --> permPacXyz00AddUser rolePacXyz00Admin --> permPacXyz00Edit +actor "Package XYZ00 Admin" as actorPacXyzAdmin +actorPacXyzAdmin -l-> rolePacXyz00Admin + +actor "Customer XYZ Admin" as actorCustXyzAdmin +actorCustXyzAdmin --> roleCustXyzAdmin + entity "Role administrators" as roleAdmins roleAdmins --> roleCustXyzOwner +actor "Any Hostmaster" as actorHostmaster +actorHostmaster --> roleAdmins + @enduml ``` -Initially, the customer's admin role gets the package owner role granted. +Initially, the customer's admin role is assigned to the package owner role. They can use the package's admin role to hand over most management functionality to a third party. The 'administrators' can get access through an assumed customer's admin role or directly by assuming the package's owner or admin role. From e97022fb020f640f7c36d35c27d71b3a3a74ceac Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 26 Jul 2022 13:19:17 +0200 Subject: [PATCH 09/54] add documentation to backup+restore the database from PostgreSQL docker container --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 8ff1af54..227a0eb6 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,16 @@ Remove the PostgreSQL container: After the PostgreSQL container is removed, you need to create it again as shown in "Create and run ..." above. +Given the container is running, to create a backup in ~/backup, run: + + docker exec -i hsadmin-ng-postgres /usr/bin/pg_dump --clean --create -U postgres postgres | gzip -9 > ~/backup/hsadmin-ng-postgres2.sql.gz + + +Again, given the container is running, to restore the backup from ~/backup, run: + + gunzip --stdout --keep ~/backup/hsadmin-ng-postgres.sql.gz | docker exec -i hsadmin-ng-postgres psql -U postgres -d postgres + + ### Markdown with PlantUML plugin Can you see the following diagram? From bafae52ce5af8f0b217a0b79e60d320afa4330b3 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 27 Jul 2022 12:32:54 +0200 Subject: [PATCH 10/54] _rv query performance experiments --- sql/10-rbac-base.sql | 15 ++++++-- sql/21-hs-customer.sql | 12 ++++++ sql/22-hs-packages.sql | 15 +++++++- sql/23-hs-unixuser.sql | 23 +++++++++-- sql/24-hs-domain.sql | 24 ++++++++++-- sql/25-hs-emailaddress.sql | 32 +++++++++++----- sql/28--hs-tests.sql | 78 ++++++++++++++++++++++++++++++++------ 7 files changed, 165 insertions(+), 34 deletions(-) diff --git a/sql/10-rbac-base.sql b/sql/10-rbac-base.sql index 2219c00c..07120878 100644 --- a/sql/10-rbac-base.sql +++ b/sql/10-rbac-base.sql @@ -83,11 +83,15 @@ END; $$; -- DROP TABLE IF EXISTS RbacPermission; CREATE TABLE RbacPermission ( uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, - objectUuid uuid not null, + objectUuid uuid not null references RbacObject, op RbacOp not null, unique (objectUuid, op) ); +-- SET SESSION SESSION AUTHORIZATION DEFAULT; +-- alter table rbacpermission add constraint rbacpermission_objectuuid_fkey foreign key (objectUuid) references rbacobject(uuid); +-- alter table rbacpermission drop constraint rbacpermission_objectuuid; + CREATE OR REPLACE FUNCTION hasPermission(forObjectUuid uuid, forOp RbacOp) RETURNS bool LANGUAGE sql AS $$ @@ -311,9 +315,9 @@ set local session authorization default; CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( requiredOp RbacOp, - forObjectTable varchar, -- TODO: test performance in joins! + forObjectTable varchar, -- reduces the result set, but is not really faster when used in restricted view subjectIds uuid[], - maxObjects integer = 16000) + maxObjects integer = 8000) RETURNS SETOF uuid RETURNS NULL ON NULL INPUT LANGUAGE plpgsql AS $$ @@ -336,7 +340,8 @@ CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( ) as granted JOIN RbacPermission perm ON granted.descendantUuid=perm.uuid AND perm.op IN ('*', requiredOp) - JOIN RbacObject obj ON obj.uuid=perm.objectUuid AND obj.objectTable=forObjectTable; + JOIN RbacObject obj ON obj.uuid=perm.objectUuid AND obj.objectTable=forObjectTable + LIMIT maxObjects+1; foundRows = lastRowCount(); IF foundRows > maxObjects THEN @@ -350,8 +355,10 @@ $$; SET SESSION AUTHORIZATION DEFAULT; CREATE ROLE admin; +GRANT USAGE ON SCHEMA public TO admin; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin; CREATE ROLE restricted; +GRANT USAGE ON SCHEMA public TO restricted; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; abort; diff --git a/sql/21-hs-customer.sql b/sql/21-hs-customer.sql index 3d861d19..0c77dea6 100644 --- a/sql/21-hs-customer.sql +++ b/sql/21-hs-customer.sql @@ -108,6 +108,17 @@ CREATE TRIGGER deleteRbacRulesForCustomer_Trigger -- create RBAC restricted view +-- automatically updatable, but slow with WHERE IN +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS customer_rv; +CREATE OR REPLACE VIEW customer_rv AS + SELECT DISTINCT target.* + FROM customer AS target + WHERE target.uuid IN (SELECT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'customer', currentSubjectIds())); +GRANT ALL PRIVILEGES ON customer_rv TO restricted; + +-- not automatically updatable, but fast with JOIN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE customer ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS customer_rv; @@ -121,6 +132,7 @@ GRANT ALL PRIVILEGES ON customer_rv TO restricted; -- generate Customer test data +SET SESSION SESSION AUTHORIZATION DEFAULT; DO LANGUAGE plpgsql $$ DECLARE currentTask varchar; diff --git a/sql/22-hs-packages.sql b/sql/22-hs-packages.sql index 676f9b1f..483725b4 100644 --- a/sql/22-hs-packages.sql +++ b/sql/22-hs-packages.sql @@ -98,8 +98,19 @@ CREATE TRIGGER deleteRbacRulesForPackage_Trigger BEFORE DELETE ON customer FOR EACH ROW EXECUTE PROCEDURE deleteRbacRulesForPackage(); --- create RBAC restricted view +-- create RBAC-restricted view +-- automatically updatable, but slow with WHERE IN +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE package ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS package_rv; +CREATE OR REPLACE VIEW package_rv AS + SELECT DISTINCT target.* + FROM package AS target + WHERE target.uuid IN (SELECT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'package', currentSubjectIds())); +GRANT ALL PRIVILEGES ON package_rv TO restricted; + +-- not automatically updatable, but fast with JOIN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE package ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS package_rv; @@ -124,6 +135,8 @@ DO LANGUAGE plpgsql $$ SET hsadminng.currentUser TO ''; FOR cust IN (SELECT * FROM customer) LOOP + -- CONTINUE WHEN cust.reference < 18000; + FOR t IN 0..randominrange(1, 2) LOOP pacName = cust.prefix || TO_CHAR(t, 'fm00'); currentTask = 'creating RBAC test package #'|| pacName || ' for customer ' || cust.prefix || ' #' || cust.uuid; diff --git a/sql/23-hs-unixuser.sql b/sql/23-hs-unixuser.sql index dcc992a6..cfeccfb0 100644 --- a/sql/23-hs-unixuser.sql +++ b/sql/23-hs-unixuser.sql @@ -101,8 +101,19 @@ CREATE TRIGGER createRbacRulesForUnixUser_Trigger -- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForUnixUser() --- create RBAC restricted view +-- create RBAC-restricted view +-- automatically updatable, but slow with WHERE IN +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS unixuser_rv; +CREATE OR REPLACE VIEW unixuser_rv AS + SELECT DISTINCT target.* + FROM unixuser AS target + WHERE target.uuid IN (SELECT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'unixuser', currentSubjectIds())); +GRANT ALL PRIVILEGES ON unixuser_rv TO restricted; + +-- not automatically updatable, but fast with JOIN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS unixuser_rv; @@ -118,13 +129,19 @@ GRANT ALL PRIVILEGES ON unixuser_rv TO restricted; DO LANGUAGE plpgsql $$ DECLARE - pac package; + pac record; pacAdmin varchar; currentTask varchar; BEGIN SET hsadminng.currentUser TO ''; - FOR pac IN (SELECT * FROM package) LOOP + FOR pac IN ( + SELECT p.uuid, p.name + FROM package p + JOIN customer c ON p.customeruuid = c.uuid + -- WHERE c.reference >= 18000 + ) LOOP + FOR t IN 0..9 LOOP currentTask = 'creating RBAC test unixuser #' || t || ' for package ' || pac.name|| ' #' || pac.uuid; RAISE NOTICE 'task: %', currentTask; diff --git a/sql/24-hs-domain.sql b/sql/24-hs-domain.sql index ce730f40..856d55f6 100644 --- a/sql/24-hs-domain.sql +++ b/sql/24-hs-domain.sql @@ -85,8 +85,19 @@ CREATE TRIGGER createRbacRulesForDomain_Trigger -- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForDomain() --- create RBAC restricted view +-- create RBAC-restricted view +-- automatically updatable, but slow with WHERE IN +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS domain_rv; +CREATE OR REPLACE VIEW domain_rv AS + SELECT DISTINCT target.* + FROM Domain AS target + WHERE target.uuid IN (SELECT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'domain', currentSubjectIds())); +GRANT ALL PRIVILEGES ON domain_rv TO restricted; + +-- not automatically updatable, but fast with JOIN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS domain_rv; @@ -97,19 +108,24 @@ CREATE OR REPLACE VIEW domain_rv AS ON target.uuid = allowedObjId; GRANT ALL PRIVILEGES ON domain_rv TO restricted; - -- generate Domain test data DO LANGUAGE plpgsql $$ DECLARE - uu unixuser; + uu record; pac package; pacAdmin varchar; currentTask varchar; BEGIN SET hsadminng.currentUser TO ''; - FOR uu IN (SELECT * FROM unixuser) LOOP + FOR uu IN ( + SELECT u.uuid, u.name, u.packageuuid, c.reference + FROM unixuser u + JOIN package p ON u.packageuuid = p.uuid + JOIN customer c ON p.customeruuid = c.uuid + -- WHERE c.reference >= 18000 + ) LOOP IF ( random() < 0.3 ) THEN FOR t IN 0..1 LOOP currentTask = 'creating RBAC test Domain #' || t || ' for UnixUser ' || uu.name|| ' #' || uu.uuid; diff --git a/sql/25-hs-emailaddress.sql b/sql/25-hs-emailaddress.sql index 49c25d06..df9d575c 100644 --- a/sql/25-hs-emailaddress.sql +++ b/sql/25-hs-emailaddress.sql @@ -73,39 +73,51 @@ CREATE TRIGGER createRbacRulesForEMailAddress_Trigger -- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForEMailAddress() --- create RBAC restricted view +-- create RBAC-restricted view +-- automatically updatable, but slow with WHERE IN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS EMailAddress_rv; CREATE OR REPLACE VIEW EMailAddress_rv AS SELECT DISTINCT target.* FROM EMailAddress AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'emailaddress', currentSubjectIds()) AS allowedObjId - ON target.uuid = allowedObjId; + WHERE target.uuid IN (SELECT DISTINCT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'emailaddress', currentSubjectIds())); GRANT ALL PRIVILEGES ON EMailAddress_rv TO restricted; +-- not automatically updatable, but fast with JOIN +SET SESSION SESSION AUTHORIZATION DEFAULT; +ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS EMailAddress_rv; +CREATE OR REPLACE VIEW EMailAddress_rv AS + SELECT target.* + FROM EMailAddress AS target + WHERE target.uuid IN (SELECT DISTINCT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'emailaddress', currentSubjectIds())); +GRANT ALL PRIVILEGES ON EMailAddress_rv TO restricted; -- generate EMailAddress test data DO LANGUAGE plpgsql $$ DECLARE - pac package; - uu unixuser; - dom domain; + dom record; pacAdmin varchar; currentTask varchar; BEGIN SET hsadminng.currentUser TO ''; - FOR dom IN (SELECT * FROM domain) LOOP + FOR dom IN ( + SELECT d.uuid, d.name, p.name as packageName + FROM domain d + JOIN unixuser u ON u.uuid = d.unixuseruuid + JOIN package p ON u.packageuuid = p.uuid + JOIN customer c ON p.customeruuid = c.uuid + -- WHERE c.reference >= 18000 + ) LOOP FOR t IN 0..4 LOOP currentTask = 'creating RBAC test EMailAddress #' || t || ' for Domain ' || dom.name; RAISE NOTICE 'task: %', currentTask; - SELECT * FROM unixuser WHERE uuid=dom.unixuserUuid INTO uu; - SELECT * FROM package WHERE uuid=uu.packageUuid INTO pac; - pacAdmin = 'admin@' || pac.name || '.example.com'; + pacAdmin = 'admin@' || dom.packageName || '.example.com'; SET LOCAL hsadminng.currentUser TO pacAdmin; SET LOCAL hsadminng.assumedRoles = ''; SET LOCAL hsadminng.currentTask TO currentTask; diff --git a/sql/28--hs-tests.sql b/sql/28--hs-tests.sql index ae1e73e7..6b095349 100644 --- a/sql/28--hs-tests.sql +++ b/sql/28--hs-tests.sql @@ -1,73 +1,113 @@ +ABORT; +SET SESSION SESSION AUTHORIZATION DEFAULT; + +-- there are some random ractors in test data generation, thus a range has to be accepted +CREATE OR REPLACE PROCEDURE expectBetween(actualCount integer, expectedFrom integer, expectedTo integer) + LANGUAGE plpgsql AS $$ +BEGIN + IF NOT actualCount BETWEEN expectedFrom AND expectedTo THEN + RAISE EXCEPTION 'count expected to be between % and %, but got %', expectedFrom, expectedTo, actualCount; + END IF; +END; $$; DO LANGUAGE plpgsql $$ +DECLARE + resultCount integer; BEGIN -- hostmaster accessing a single customer SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; - PERFORM * from customer_rv c where c.prefix='aab'; + SET LOCAL hsadminng.assumedRoles = ''; + -- SELECT * + SELECT count(*) INTO resultCount + from customer_rv c + where c.prefix='aab'; + call expectBetween(resultCount, 1, 1); -- hostmaster listing all customers SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; SET LOCAL hsadminng.assumedRoles = ''; - PERFORM * FROM customer_rv; + -- SELECT * + SELECT count(*) INTO resultCount + FROM customer_rv; + call expectBetween(resultCount, 10, 20000); -- customer admin listing all their packages SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'admin@aae.example.com'; SET LOCAL hsadminng.assumedRoles = ''; - PERFORM * FROM package_rv; + -- SELECT * + SELECT count(*) INTO resultCount + FROM package_rv; + call expectBetween(resultCount, 2, 10); -- cutomer admin listing all their unix users SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'admin@aae.example.com'; SET LOCAL hsadminng.assumedRoles = ''; - PERFORM * FROM unixuser_rv; + -- SELECT * + SELECT count(*) INTO resultCount + FROM unixuser_rv; + call expectBetween(resultCount, 20, 50); -- hostsharing admin assuming customer role and listing all accessible packages SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; SET LOCAL hsadminng.assumedRoles = 'customer#aaa.admin;customer#aab.admin'; - PERFORM * FROM package_rv p; + -- SELECT * + SELECT count(*) INTO resultCount + FROM package_rv p; + call expectBetween(resultCount, 2, 10); -- hostsharing admin assuming two customer admin roles and listing all accessible unixusers SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin'; - PERFORM c.prefix, c.reference, uu.* + -- SELECT c.prefix, c.reference, uu.* + SELECT count(*) INTO resultCount FROM unixuser_rv uu JOIN package_rv p ON p.uuid = uu.packageuuid JOIN customer_rv c ON c.uuid = p.customeruuid; + call expectBetween(resultCount, 30, 50); -- hostsharing admin assuming two customer admin roles and listing all accessible domains + -- ABORT; START TRANSACTION; SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; SET LOCAL hsadminng.assumedRoles = 'customer#aac.admin;customer#aad.admin'; - PERFORM p.name, uu.name, dom.name + -- SELECT p.name, uu.name, dom.name + SELECT count(*) INTO resultCount FROM domain_rv dom JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid JOIN package_rv p ON p.uuid = uu.packageuuid JOIN customer_rv c ON c.uuid = p.customeruuid; + call expectBetween(resultCount, 30, 50); -- hostsharing admin assuming two customer admin roles and listing all accessible email addresses + -- ABORT; START TRANSACTION; SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; - SET LOCAL hsadminng.assumedRoles = 'customer#aad.admin;customer#aae.admin'; - PERFORM c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address" + SET LOCAL hsadminng.assumedRoles = 'customer#aae.admin;customer#aaf.admin'; + SELECT c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address" + -- SELECT count(*) INTO resultCount FROM emailaddress_rv ema JOIN domain_rv dom ON dom.uuid = ema.domainuuid JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid JOIN package_rv p ON p.uuid = uu.packageuuid JOIN customer_rv c ON c.uuid = p.customeruuid; + call expectBetween(resultCount, 100, 300); + + -- ~170ms END; $$; /* === with 7000 customers === -1. [2022-07-26 09:17:19] completed in 801 ms -2. [2022-07-26 09:17:32] completed in 649 ms -3. [2022-07-26 09:17:51] completed in 670 ms +1. 7105 vs 801 ms +2. 960 vs. 649 ms +3. 970 vs. 670 ms no count required factor table 1 7 000 7 000 1.000 customers @@ -76,5 +116,19 @@ no count required factor table 4 105 206 100 000 1.052 domain 5 526 030 500 000 1.052 emailaddress +=== with 10000 customers (+43%) === + +1. 7491 vs. 1189 ms (-1%) +2. 1049 ms (+31%) +3. 1028 ms (+53%) +in average +9,33% + +no count required factor table +1 10 000 7 000 1.429 customers +2 24 904 15 000 1.660 packages +3 249 040 150 000 1.660 unixuser +4 149 946 100 000 1.499 domain +5 749 730 500 000 1.499 emailaddress + */ From 6f6320565c04151ff8ef393b0c07aaa6e1c34431 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 27 Jul 2022 13:05:19 +0200 Subject: [PATCH 11/54] the _rv query with WHERE IN was faster after all, removing the JOIN variant --- sql/00-util.sql | 14 ++++++++++++++ sql/21-hs-customer.sql | 15 +-------------- sql/22-hs-packages.sql | 16 +--------------- sql/23-hs-unixuser.sql | 16 ++-------------- sql/24-hs-domain.sql | 14 +------------- sql/25-hs-emailaddress.sql | 14 +------------- sql/28--hs-tests.sql | 13 ++----------- 7 files changed, 22 insertions(+), 80 deletions(-) diff --git a/sql/00-util.sql b/sql/00-util.sql index 06755f11..7f39452e 100644 --- a/sql/00-util.sql +++ b/sql/00-util.sql @@ -47,3 +47,17 @@ BEGIN END; $$; select * from randomInRange(0, 4); + + +-- ======================================================== +-- Test helpers +-- -------------------------------------------------------- + +-- there are some random ractors in test data generation, thus a range has to be accepted +CREATE OR REPLACE PROCEDURE expectBetween(actualCount integer, expectedFrom integer, expectedTo integer) + LANGUAGE plpgsql AS $$ +BEGIN + IF NOT actualCount BETWEEN expectedFrom AND expectedTo THEN + RAISE EXCEPTION 'count expected to be between % and %, but got %', expectedFrom, expectedTo, actualCount; + END IF; +END; $$; diff --git a/sql/21-hs-customer.sql b/sql/21-hs-customer.sql index 0c77dea6..83612e9b 100644 --- a/sql/21-hs-customer.sql +++ b/sql/21-hs-customer.sql @@ -107,26 +107,13 @@ CREATE TRIGGER deleteRbacRulesForCustomer_Trigger -- create RBAC restricted view - --- automatically updatable, but slow with WHERE IN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE customer ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS customer_rv; CREATE OR REPLACE VIEW customer_rv AS SELECT DISTINCT target.* FROM customer AS target - WHERE target.uuid IN (SELECT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'customer', currentSubjectIds())); -GRANT ALL PRIVILEGES ON customer_rv TO restricted; - --- not automatically updatable, but fast with JOIN -SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE customer ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS customer_rv; -CREATE OR REPLACE VIEW customer_rv AS - SELECT DISTINCT target.* - FROM customer AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'customer', currentSubjectIds()) AS allowedObjId - ON target.uuid = allowedObjId; + WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'customer', currentSubjectIds())); GRANT ALL PRIVILEGES ON customer_rv TO restricted; diff --git a/sql/22-hs-packages.sql b/sql/22-hs-packages.sql index 483725b4..3f21ae45 100644 --- a/sql/22-hs-packages.sql +++ b/sql/22-hs-packages.sql @@ -99,29 +99,15 @@ CREATE TRIGGER deleteRbacRulesForPackage_Trigger FOR EACH ROW EXECUTE PROCEDURE deleteRbacRulesForPackage(); -- create RBAC-restricted view - --- automatically updatable, but slow with WHERE IN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE package ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS package_rv; CREATE OR REPLACE VIEW package_rv AS SELECT DISTINCT target.* FROM package AS target - WHERE target.uuid IN (SELECT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'package', currentSubjectIds())); + WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'package', currentSubjectIds())); GRANT ALL PRIVILEGES ON package_rv TO restricted; --- not automatically updatable, but fast with JOIN -SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE package ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS package_rv; -CREATE OR REPLACE VIEW package_rv AS - SELECT DISTINCT target.* - FROM package AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'package', currentSubjectIds()) AS allowedObjId - ON target.uuid = allowedObjId; -GRANT ALL PRIVILEGES ON package_rv TO restricted; - - -- generate Package test data diff --git a/sql/23-hs-unixuser.sql b/sql/23-hs-unixuser.sql index cfeccfb0..44392104 100644 --- a/sql/23-hs-unixuser.sql +++ b/sql/23-hs-unixuser.sql @@ -8,6 +8,7 @@ SET SESSION SESSION AUTHORIZATION DEFAULT ; CREATE TABLE IF NOT EXISTS UnixUser ( uuid uuid UNIQUE REFERENCES RbacObject(uuid), name character varying(32), + comment character varying(96), packageUuid uuid REFERENCES package(uuid) ); @@ -102,26 +103,13 @@ CREATE TRIGGER createRbacRulesForUnixUser_Trigger -- create RBAC-restricted view - --- automatically updatable, but slow with WHERE IN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS unixuser_rv; CREATE OR REPLACE VIEW unixuser_rv AS SELECT DISTINCT target.* FROM unixuser AS target - WHERE target.uuid IN (SELECT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'unixuser', currentSubjectIds())); -GRANT ALL PRIVILEGES ON unixuser_rv TO restricted; - --- not automatically updatable, but fast with JOIN -SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS unixuser_rv; -CREATE OR REPLACE VIEW unixuser_rv AS - SELECT DISTINCT target.* - FROM unixuser AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'unixuser', currentSubjectIds()) AS allowedObjId - ON target.uuid = allowedObjId; + WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'unixuser', currentSubjectIds())); GRANT ALL PRIVILEGES ON unixuser_rv TO restricted; diff --git a/sql/24-hs-domain.sql b/sql/24-hs-domain.sql index 856d55f6..e9fbc142 100644 --- a/sql/24-hs-domain.sql +++ b/sql/24-hs-domain.sql @@ -86,27 +86,15 @@ CREATE TRIGGER createRbacRulesForDomain_Trigger -- create RBAC-restricted view - --- automatically updatable, but slow with WHERE IN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS domain_rv; CREATE OR REPLACE VIEW domain_rv AS SELECT DISTINCT target.* FROM Domain AS target - WHERE target.uuid IN (SELECT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'domain', currentSubjectIds())); + WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'domain', currentSubjectIds())); GRANT ALL PRIVILEGES ON domain_rv TO restricted; --- not automatically updatable, but fast with JOIN -SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS domain_rv; -CREATE OR REPLACE VIEW domain_rv AS - SELECT DISTINCT target.* - FROM Domain AS target - JOIN queryAccessibleObjectUuidsOfSubjectIds( 'view', 'domain', currentSubjectIds()) AS allowedObjId - ON target.uuid = allowedObjId; -GRANT ALL PRIVILEGES ON domain_rv TO restricted; -- generate Domain test data diff --git a/sql/25-hs-emailaddress.sql b/sql/25-hs-emailaddress.sql index df9d575c..7ff46b5a 100644 --- a/sql/25-hs-emailaddress.sql +++ b/sql/25-hs-emailaddress.sql @@ -74,25 +74,13 @@ CREATE TRIGGER createRbacRulesForEMailAddress_Trigger -- create RBAC-restricted view - --- automatically updatable, but slow with WHERE IN SET SESSION SESSION AUTHORIZATION DEFAULT; ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS EMailAddress_rv; CREATE OR REPLACE VIEW EMailAddress_rv AS SELECT DISTINCT target.* FROM EMailAddress AS target - WHERE target.uuid IN (SELECT DISTINCT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'emailaddress', currentSubjectIds())); -GRANT ALL PRIVILEGES ON EMailAddress_rv TO restricted; - --- not automatically updatable, but fast with JOIN -SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS EMailAddress_rv; -CREATE OR REPLACE VIEW EMailAddress_rv AS - SELECT target.* - FROM EMailAddress AS target - WHERE target.uuid IN (SELECT DISTINCT uuid FROM queryAccessibleObjectUuidsOfSubjectIds( 'view', 'emailaddress', currentSubjectIds())); + WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'emailaddress', currentSubjectIds())); GRANT ALL PRIVILEGES ON EMailAddress_rv TO restricted; -- generate EMailAddress test data diff --git a/sql/28--hs-tests.sql b/sql/28--hs-tests.sql index 6b095349..e52a2ca3 100644 --- a/sql/28--hs-tests.sql +++ b/sql/28--hs-tests.sql @@ -1,15 +1,6 @@ ABORT; SET SESSION SESSION AUTHORIZATION DEFAULT; --- there are some random ractors in test data generation, thus a range has to be accepted -CREATE OR REPLACE PROCEDURE expectBetween(actualCount integer, expectedFrom integer, expectedTo integer) - LANGUAGE plpgsql AS $$ -BEGIN - IF NOT actualCount BETWEEN expectedFrom AND expectedTo THEN - RAISE EXCEPTION 'count expected to be between % and %, but got %', expectedFrom, expectedTo, actualCount; - END IF; -END; $$; - DO LANGUAGE plpgsql $$ DECLARE resultCount integer; @@ -90,8 +81,8 @@ BEGIN SET SESSION SESSION AUTHORIZATION restricted; SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; SET LOCAL hsadminng.assumedRoles = 'customer#aae.admin;customer#aaf.admin'; - SELECT c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address" - -- SELECT count(*) INTO resultCount + -- SELECT c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address" + SELECT count(*) INTO resultCount FROM emailaddress_rv ema JOIN domain_rv dom ON dom.uuid = ema.domainuuid JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid From 1dde6b26092ca9ce64ce9d1f21af151c0a7ef3ad Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 27 Jul 2022 19:54:05 +0200 Subject: [PATCH 12/54] introduce referential integrity for role identification - part 1 --- sql/10-rbac-base.sql | 171 +++++++++++++++++++++-------------- sql/12-rbac-role-builder.sql | 48 ++++------ sql/20-hs-base.sql | 22 ++++- sql/21-hs-customer.sql | 28 +++--- sql/22-hs-packages.sql | 41 +++++---- sql/23-hs-unixuser.sql | 49 +++++----- sql/24-hs-domain.sql | 41 +++++---- sql/25-hs-emailaddress.sql | 37 ++++---- 8 files changed, 246 insertions(+), 191 deletions(-) diff --git a/sql/10-rbac-base.sql b/sql/10-rbac-base.sql index 07120878..68124563 100644 --- a/sql/10-rbac-base.sql +++ b/sql/10-rbac-base.sql @@ -29,10 +29,21 @@ CREATE TABLE RbacUser name varchar(63) not null unique ); +-- DROP TABLE IF EXISTS RbacObject; +CREATE TABLE RbacObject +( + uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4(), + objectTable varchar(64) not null, + unique (objectTable, uuid) +); + +CREATE TYPE RbacRoleType AS ENUM ('owner', 'admin', 'tenant'); + CREATE TABLE RbacRole ( uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, - name varchar(63) not null unique + objectUuid uuid references RbacObject(uuid) not null, + roleType RbacRoleType not null ); CREATE TABLE RbacGrants @@ -56,14 +67,6 @@ CREATE DOMAIN RbacOp AS VARCHAR(67) OR VALUE ~ '^add-[a-z]+$' ); --- DROP TABLE IF EXISTS RbacObject; -CREATE TABLE RbacObject -( - uuid uuid UNIQUE DEFAULT uuid_generate_v4(), - objectTable varchar(64) not null, - unique (objectTable, uuid) -); - CREATE OR REPLACE FUNCTION createRbacObject() RETURNS trigger LANGUAGE plpgsql STRICT AS $$ @@ -144,7 +147,51 @@ BEGIN END; $$; -CREATE OR REPLACE FUNCTION createRole(roleName varchar) +CREATE TYPE RbacRoleDescriptor AS + ( + objectTable varchar(63), -- TODO: needed? remove? + objectUuid uuid, + roleType RbacRoleType + ); + +-- TODO: this ... +CREATE OR REPLACE FUNCTION roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType ) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + STABLE LEAKPROOF + LANGUAGE plpgsql AS $$ +BEGIN + RETURN ROW(objectTable, objectUuid, roleType); +END; $$; + +CREATE FUNCTION new_emp() RETURNS emp AS $$ +SELECT text 'None' AS name, + 1000.0 AS salary, + 25 AS age, + point '(2,2)' AS cubicle; +$$ LANGUAGE SQL; + +DO LANGUAGE plpgsql $$ +DECLARE + roleDesc RbacRoleDescriptor; +BEGIN + select * FROM roleDescriptor('global', gen_random_uuid(), 'admin') into roleDesc; + RAISE NOTICE 'result: % % %', roleDesc.objecttable, roleDesc.objectuuid, roleDesc.roletype; +END; $$; + +-- TODO: ... or that? +CREATE OR REPLACE FUNCTION roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType ) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + -- STABLE LEAKPROOF + LANGUAGE sql AS $$ + SELECT objectTable, objectUuid, roleType::RbacRoleType; + $$; + + + + +CREATE OR REPLACE FUNCTION createRole(roleDescriptor RbacRoleDescriptor) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE plpgsql AS $$ @@ -152,10 +199,7 @@ declare referenceId uuid; BEGIN INSERT INTO RbacReference (type) VALUES ('RbacRole') RETURNING uuid INTO referenceId; - INSERT INTO RbacRole (uuid, name) VALUES (referenceId, roleName); - IF (referenceId IS NULL) THEN - RAISE EXCEPTION 'referenceId for roleName "%" is unexpectedly null', roleName; - end if; + INSERT INTO RbacRole (uuid, objectUuid, roleType) VALUES (referenceId, roleDescriptor.objectUuid, roleDescriptor.roleType); return referenceId; END; $$; @@ -168,27 +212,27 @@ BEGIN END; $$; -CREATE OR REPLACE FUNCTION findRoleId(roleName varchar) +CREATE OR REPLACE FUNCTION findRoleId(roleDescriptor RbacRoleDescriptor) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE sql AS $$ - SELECT uuid FROM RbacRole WHERE name = roleName + SELECT uuid FROM RbacRole WHERE objectUuid = roleDescriptor.objectUuid AND roleType = roleDescriptor.roleType; $$; -CREATE OR REPLACE FUNCTION getRoleId(roleName varchar, whenNotExists RbacWhenNotExists) +CREATE OR REPLACE FUNCTION getRoleId(roleDescriptor RbacRoleDescriptor, whenNotExists RbacWhenNotExists) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE plpgsql AS $$ DECLARE roleUuid uuid; BEGIN - roleUuid = findRoleId(roleName); + roleUuid = findRoleId(roleDescriptor); IF ( roleUuid IS NULL ) THEN IF ( whenNotExists = 'fail') THEN - RAISE EXCEPTION 'RbacRole with name="%" not found', roleName; + RAISE EXCEPTION 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType; END IF; IF ( whenNotExists = 'create') THEN - roleUuid = createRole(roleName); + roleUuid = createRole(roleDescriptor); END IF; END IF; return roleUuid; @@ -204,6 +248,7 @@ DECLARE refId uuid; permissionIds uuid[] = ARRAY[]::uuid[]; BEGIN + RAISE NOTICE 'createPermission for: % %', forObjectUuid, permitOps; IF ( forObjectUuid IS NULL ) THEN RAISE EXCEPTION 'forObjectUuid must not be null'; END IF; @@ -214,16 +259,20 @@ BEGIN FOR i IN array_lower(permitOps, 1)..array_upper(permitOps, 1) LOOP refId = (SELECT uuid FROM RbacPermission WHERE objectUuid=forObjectUuid AND op=permitOps[i]); IF (refId IS NULL) THEN + RAISE NOTICE 'createPermission: % %', forObjectUuid, permitOps[i]; INSERT INTO RbacReference ("type") VALUES ('RbacPermission') RETURNING uuid INTO refId; INSERT INTO RbacPermission (uuid, objectUuid, op) VALUES (refId, forObjectUuid, permitOps[i]); END IF; + RAISE NOTICE 'addPermission: %', refId; permissionIds = permissionIds || refId; END LOOP; + + RAISE NOTICE 'createPermissions returning: %', permissionIds; return permissionIds; END; $$; -CREATE OR REPLACE FUNCTION findPermissionId(forObjectTable varchar, forObjectUuid uuid, forOp RbacOp) +CREATE OR REPLACE FUNCTION findPermissionId(forObjectUuid uuid, forOp RbacOp) RETURNS uuid RETURNS NULL ON NULL INPUT STABLE LEAKPROOF @@ -248,6 +297,7 @@ END; $$; CREATE OR REPLACE PROCEDURE grantPermissionsToRole(roleUuid uuid, permissionIds uuid[]) LANGUAGE plpgsql AS $$ BEGIN + RAISE NOTICE 'grantPermissionsToRole: % -> %', roleUuid, permissionIds; FOR i IN array_lower(permissionIds, 1)..array_upper(permissionIds, 1) LOOP perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole'); perform assertReferenceType('permissionId (descendant)', permissionIds[i], 'RbacPermission'); @@ -295,21 +345,6 @@ BEGIN ON CONFLICT DO NOTHING ; -- TODO: remove? END; $$; -abort; -set local session authorization default; - -CREATE OR REPLACE FUNCTION nextLevel(level integer, maxDepth integer) - RETURNS INTEGER - LANGUAGE plpgsql AS $$ - BEGIN - IF (level > maxDepth) THEN - RAISE WARNING 'Role assignment depth exceeded %/%.', level, maxDepth; - END IF; - RETURN level+1; - END; -$$; - - abort; set local session authorization default; @@ -444,7 +479,7 @@ begin transaction; end transaction; --- - +/* CREATE OR REPLACE FUNCTION queryAllPermissionsOfSubjectId(subjectId uuid) -- TODO: remove? RETURNS SETOF RbacPermission RETURNS NULL ON NULL INPUT @@ -469,7 +504,7 @@ CREATE OR REPLACE FUNCTION queryAllPermissionsOfSubjectId(subjectId uuid) -- TOD FROM grants ); -$$; +$$;*/ --- @@ -643,43 +678,39 @@ CREATE OR REPLACE FUNCTION currentSubjectIds() STABLE LEAKPROOF LANGUAGE plpgsql AS $$ DECLARE - assumedRoles VARCHAR(63)[]; - currentUserId uuid; - assumedRoleIds uuid[]; - assumedRoleId uuid; + currentUserId uuid; + roleNames VARCHAR(63)[]; + roleName VARCHAR(63); + objectTableToAssume VARCHAR(63); + objectNameToAssume VARCHAR(63); + objectUuidToAssume uuid; + roleTypeToAssume RbacRoleType; + roleIdsToAssume uuid[]; + roleUuidToAssume uuid; BEGIN currentUserId := currentUserId(); - assumedRoles := assumedRoles(); - IF ( CARDINALITY(assumedRoles) = 0 ) THEN + roleNames := assumedRoles(); + IF ( CARDINALITY(roleNames) = 0 ) THEN RETURN ARRAY[currentUserId]; END IF; - RAISE NOTICE 'assuming roles: %', assumedRoles; + RAISE NOTICE 'assuming roles: %', roleNames; - SELECT ARRAY_AGG(uuid) FROM RbacRole WHERE name = ANY(assumedRoles) INTO assumedRoleIds; - IF assumedRoleIds IS NOT NULL THEN - FOREACH assumedRoleId IN ARRAY assumedRoleIds LOOP - IF ( NOT isGranted(currentUserId, assumedRoleId) ) THEN - RAISE EXCEPTION 'user % has no permission to assume role %', currentUser(), assumedRoleId; - END IF; - END LOOP; - END IF; - RETURN assumedRoleIds; -END; $$; + FOREACH roleName IN ARRAY roleNames LOOP + roleName = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), '.')); + objectTableToAssume = split_part(roleName, '#', 1); + objectNameToAssume = split_part(roleName, '#', 2); + roleTypeToAssume = split_part(roleName, '#', 3); -rollback; -set session authorization default; -CREATE OR REPLACE FUNCTION maxGrantDepth() - RETURNS integer - STABLE LEAKPROOF - LANGUAGE plpgsql AS $$ -DECLARE - maxGrantDepth VARCHAR(63); -BEGIN - BEGIN - maxGrantDepth := current_setting('hsadminng.maxGrantDepth'); - EXCEPTION WHEN OTHERS THEN - maxGrantDepth := NULL; - END; - RETURN coalesce(maxGrantDepth, '8')::integer; + -- TODO: either the result needs to be cached at least per transaction or we need to get rid of SELCT in a loop + SELECT uuid AS roleuuidToAssume + FROM RbacRole r + WHERE r.objectUuid=objectUuidToAssume AND r.roleType=roleTypeToAssume INTO roleUuidToAssume; + IF ( NOT isGranted(currentUserId, roleUuidToAssume) ) THEN + RAISE EXCEPTION 'user % has no permission to assume role %', currentUser(), roleUuidToAssume; + END IF; + roleIdsToAssume := roleIdsToAssume || roleUuidToAssume; + END LOOP; + + RETURN roleIdsToAssume; END; $$; diff --git a/sql/12-rbac-role-builder.sql b/sql/12-rbac-role-builder.sql index fc24de10..410a97e8 100644 --- a/sql/12-rbac-role-builder.sql +++ b/sql/12-rbac-role-builder.sql @@ -4,8 +4,6 @@ -- Role-Hierarcy helper functions -- -------------------------------------------------------- -CREATE TYPE RbacRoleType AS ENUM ('owner', 'admin', 'tenant'); - -- PERMISSIONS -------------------------------------------- -- drop type RbacPermissions; @@ -29,27 +27,27 @@ CREATE TYPE RbacSuperRoles AS roleUuids uuid[] ); --- drop function beneathRoles(roleName varchar); -CREATE OR REPLACE FUNCTION beneathRoles(roleNames varchar[]) +-- drop function beneathRoles(roleDescriptors RbacRoleDescriptor[]) +CREATE OR REPLACE FUNCTION beneathRoles(roleDescriptors RbacRoleDescriptor[]) RETURNS RbacSuperRoles LANGUAGE plpgsql STRICT AS $$ DECLARE - superRoleName varchar; + superRoleDescriptor RbacRoleDescriptor; superRoleUuids uuid[] := ARRAY[]::uuid[]; BEGIN - FOREACH superRoleName IN ARRAY roleNames LOOP - superRoleUuids := superRoleUuids || getRoleId(superRoleName, 'fail'); + FOREACH superRoleDescriptor IN ARRAY roleDescriptors LOOP + superRoleUuids := superRoleUuids || getRoleId(superRoleDescriptor, 'fail'); END LOOP; RETURN ROW(superRoleUuids)::RbacSuperRoles; END; $$; --- drop function beneathRole(roleName varchar); -CREATE OR REPLACE FUNCTION beneathRole(roleName varchar) +-- drop function beneathRole(roleDescriptor RbacRoleDescriptor) +CREATE OR REPLACE FUNCTION beneathRole(roleDescriptor RbacRoleDescriptor) RETURNS RbacSuperRoles LANGUAGE plpgsql STRICT AS $$ BEGIN - RETURN beneathRoles(ARRAY[roleName]); + RETURN beneathRoles(ARRAY[roleDescriptor]); END; $$; -- drop function beneathRole(roleUuid uuid); @@ -83,12 +81,12 @@ BEGIN RETURN ROW(ARRAY[roleUuid]::uuid[])::RbacSubRoles; END; $$; --- drop FUNCTION beingItselfA(roleName varchar) -CREATE OR REPLACE FUNCTION beingItselfA(roleName varchar) +-- drop FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor) +CREATE OR REPLACE FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor) RETURNS RbacSubRoles LANGUAGE plpgsql STRICT AS $$ BEGIN - RETURN beingItselfA(getRoleId(roleName, 'fail')); + RETURN beingItselfA(getRoleId(roleDescriptor, 'fail')); END; $$; -- USERS -------------------------------------------------- @@ -126,20 +124,11 @@ END; $$; -- ROLE NAME BUILDER -------------------------------------- -CREATE OR REPLACE FUNCTION roleName(objectTable varchar, objectName varchar, roleType RbacRoleType ) - RETURNS varchar - RETURNS NULL ON NULL INPUT - STABLE LEAKPROOF - LANGUAGE plpgsql AS $$ -BEGIN - RETURN objectTable || '#' || objectName || '.' || roleType; -END; $$; - -- CREATE ROLE MAIN FUNCTION ------------------------------ CREATE OR REPLACE FUNCTION createRole( - roleName varchar, + roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, superRoles RbacSuperRoles, subRoles RbacSubRoles = null, @@ -154,8 +143,9 @@ DECLARE subRoleUuid uuid; userUuid uuid; BEGIN - RAISE NOTICE 'creating role: %', roleName; - roleUuid = createRole(roleName); + RAISE NOTICE 'will createRole for %', roleDescriptor; + RAISE NOTICE 'will createRole for % % %', roleDescriptor.objecttable, roleDescriptor.objectuuid, roleDescriptor.roletype; + roleUuid = createRole(roleDescriptor); call grantPermissionsToRole(roleUuid, permissions.permissionUuids); @@ -181,7 +171,7 @@ BEGIN END; $$; CREATE OR REPLACE FUNCTION createRole( - roleName varchar, + roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, users RbacUsers = null ) @@ -189,11 +179,11 @@ CREATE OR REPLACE FUNCTION createRole( CALLED ON NULL INPUT LANGUAGE plpgsql AS $$ BEGIN - RETURN createRole(roleName, permissions, null, null, users); + RETURN createRole(roleDescriptor, permissions, null, null, users); END; $$; CREATE OR REPLACE FUNCTION createRole( - roleName varchar, + roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, subRoles RbacSubRoles, users RbacUsers = null @@ -202,7 +192,7 @@ CREATE OR REPLACE FUNCTION createRole( CALLED ON NULL INPUT LANGUAGE plpgsql AS $$ BEGIN - RETURN createRole(roleName, permissions, null, subRoles, users); + RETURN createRole(roleDescriptor, permissions, null, subRoles, users); END; $$; diff --git a/sql/20-hs-base.sql b/sql/20-hs-base.sql index ff745295..dcd8a252 100644 --- a/sql/20-hs-base.sql +++ b/sql/20-hs-base.sql @@ -1,10 +1,30 @@ + + +CREATE TABLE Hostsharing +( + uuid uuid PRIMARY KEY REFERENCES RbacObject(uuid) +); +CREATE UNIQUE INDEX Hostsharing_Singleton ON Hostsharing ((0)); + + +INSERT INTO RbacObject (objecttable) VALUES ('hostsharing'); +INSERT INTO Hostsharing (uuid) VALUES ((SELECT uuid FROM RbacObject WHERE objectTable='hostsharing')); + +CREATE OR REPLACE FUNCTION hostsharingAdmin() + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + STABLE LEAKPROOF + LANGUAGE sql AS $$ +SELECT 'global', (SELECT uuid FROM RbacObject WHERE objectTable='hostsharing'), 'admin'::RbacRoleType; +$$; + -- create administrators role with two assigned users do language plpgsql $$ declare admins uuid ; begin - admins = createRole('administrators'); + admins = createRole(hostsharingAdmin()); call grantRoleToUser(admins, createRbacUser('mike@hostsharing.net')); call grantRoleToUser(admins, createRbacUser('sven@hostsharing.net')); commit; diff --git a/sql/21-hs-customer.sql b/sql/21-hs-customer.sql index 83612e9b..3fcb2b1e 100644 --- a/sql/21-hs-customer.sql +++ b/sql/21-hs-customer.sql @@ -17,25 +17,25 @@ CREATE TRIGGER createRbacObjectForCustomer_Trigger BEFORE INSERT ON customer FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); -CREATE OR REPLACE FUNCTION customerOwner(customerName varchar) - RETURNS varchar +CREATE OR REPLACE FUNCTION customerOwner(customer customer) + RETURNS RbacRoleDescriptor LANGUAGE plpgsql STRICT AS $$ begin - return roleName('customer', customerName, 'owner'); + return roleDescriptor('customer', customer.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION customerAdmin(customerName varchar) - RETURNS varchar +CREATE OR REPLACE FUNCTION customerAdmin(customer customer) + RETURNS RbacRoleDescriptor LANGUAGE plpgsql STRICT AS $$ begin - return roleName('customer', customerName, 'admin'); + return roleDescriptor('customer', customer.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION customerTenant(customerName varchar) - RETURNS varchar +CREATE OR REPLACE FUNCTION customerTenant(customer customer) + RETURNS RbacRoleDescriptor LANGUAGE plpgsql STRICT AS $$ begin - return roleName('customer', customerName, 'tenant'); + return roleDescriptor('customer', customer.uuid, 'tenant'); end; $$; @@ -52,14 +52,14 @@ BEGIN -- the owner role with full access for Hostsharing administrators customerOwnerUuid = createRole( - customerOwner(NEW.prefix), + customerOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole('administrators') + beneathRole(hostsharingAdmin()) ); -- the admin role for the customer's admins, who can view and add products customerAdminUuid = createRole( - customerAdmin(NEW.prefix), + customerAdmin(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view', 'add-package']), -- NO auto follow for customer owner to avoid exploding permissions for administrators withUser(NEW.adminUserName, 'create') -- implicitly ignored if null @@ -70,7 +70,7 @@ BEGIN -- the tenant role which later can be used by owners+admins of sub-objects perform createRole( - customerTenant(NEW.prefix), + customerTenant(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view']) ); @@ -130,7 +130,7 @@ DO LANGUAGE plpgsql $$ BEGIN SET hsadminng.currentUser TO ''; - FOR t IN 0..6999 LOOP + FOR t IN 0..9 LOOP currentTask = 'creating RBAC test customer #' || t; SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net'; SET LOCAL hsadminng.assumedRoles = ''; diff --git a/sql/22-hs-packages.sql b/sql/22-hs-packages.sql index 3f21ae45..cd25bc53 100644 --- a/sql/22-hs-packages.sql +++ b/sql/22-hs-packages.sql @@ -11,25 +11,30 @@ CREATE TABLE IF NOT EXISTS package ( customerUuid uuid REFERENCES customer(uuid) ); -CREATE OR REPLACE FUNCTION packageOwner(packageName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION packageOwner(pac package) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +declare + roleDesc RbacRoleDescriptor; begin - return roleName('package', packageName, 'owner'); + return roleDescriptor('package', pac.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION packageAdmin(packageName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION packageAdmin(pac package) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('package', packageName, 'admin'); + return roleDescriptor('package', pac.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION packageTenant(packageName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION packageTenant(pac package) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('package', packageName, 'tenant'); + return roleDescriptor('package', pac.uuid, 'tenant'); end; $$; @@ -54,24 +59,24 @@ BEGIN -- an owner role is created and assigned to the customer's admin role packageOwnerRoleUuid = createRole( - packageOwner(NEW.name), + packageOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(customerAdmin(parentCustomer.prefix)) + beneathRole(customerAdmin(parentCustomer)) ); -- an owner role is created and assigned to the package owner role packageAdminRoleUuid = createRole( - packageAdmin(NEW.name), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-unixuser']), + packageAdmin(NEW), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-unixuser', 'add-domain']), beneathRole(packageOwnerRoleUuid) ); -- and a package tenant role is created and assigned to the package admin as well perform createRole( - packageTenant(NEW.name), + packageTenant(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY ['view']), beneathRole(packageAdminRoleUuid), - beingItselfA(customerTenant(parentCustomer.prefix)) + beingItselfA(customerTenant(parentCustomer)) ); RETURN NEW; diff --git a/sql/23-hs-unixuser.sql b/sql/23-hs-unixuser.sql index 44392104..52ac180c 100644 --- a/sql/23-hs-unixuser.sql +++ b/sql/23-hs-unixuser.sql @@ -12,25 +12,28 @@ CREATE TABLE IF NOT EXISTS UnixUser ( packageUuid uuid REFERENCES package(uuid) ); -CREATE OR REPLACE FUNCTION unixUserOwner(unixUserName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION unixUserOwner(uu UnixUser) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('unixuser', unixUserName, 'owner'); + return roleDescriptor('unixuser', uu.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION unixUserAdmin(unixUserName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION unixUserAdmin(uu UnixUser) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('unixuser', unixUserName, 'admin'); + return roleDescriptor('unixuser', uu.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION unixUserTenant(unixUserName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION unixUserTenant(uu UnixUser) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('unixuser', unixUserName, 'tenant'); + return roleDescriptor('unixuser', uu.uuid, 'tenant'); end; $$; CREATE OR REPLACE FUNCTION createUnixUserTenantRoleIfNotExists(unixUser UnixUser) @@ -38,19 +41,19 @@ CREATE OR REPLACE FUNCTION createUnixUserTenantRoleIfNotExists(unixUser UnixUser RETURNS NULL ON NULL INPUT LANGUAGE plpgsql AS $$ DECLARE - unixUserTenantRoleName varchar; + unixUserTenantRoleDesc RbacRoleDescriptor; unixUserTenantRoleUuid uuid; BEGIN - unixUserTenantRoleName = unixUserTenant(unixUser.name); - unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleName); + unixUserTenantRoleDesc = unixUserTenant(unixUser); + unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleDesc); IF unixUserTenantRoleUuid IS NOT NULL THEN RETURN unixUserTenantRoleUuid; END IF; RETURN createRole( - unixUserTenantRoleName, - grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => ARRAY['edit', 'add-domain']), - beneathRole(unixUserAdmin(unixUser.name)) + unixUserTenantRoleDesc, + grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => ARRAY['view']), + beneathRole(unixUserAdmin(unixUser)) ); END; $$; @@ -76,17 +79,17 @@ BEGIN -- an owner role is created and assigned to the package's admin group unixuserOwnerRoleId = createRole( - unixUserOwner(NEW.name), + unixUserOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(packageAdmin(parentPackage.name)) + beneathRole(packageAdmin(parentPackage)) ); -- and a unixuser admin role is created and assigned to the unixuser owner as well unixuserAdminRoleId = createRole( - unixUserAdmin(NEW.name), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-domain']), + unixUserAdmin(NEW), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit']), beneathRole(unixuserOwnerRoleId), - beingItselfA(packageTenant(parentPackage.name)) + beingItselfA(packageTenant(parentPackage)) ); -- a tenent role is only created on demand diff --git a/sql/24-hs-domain.sql b/sql/24-hs-domain.sql index e9fbc142..b8588913 100644 --- a/sql/24-hs-domain.sql +++ b/sql/24-hs-domain.sql @@ -16,25 +16,28 @@ CREATE TRIGGER createRbacObjectForDomain_Trigger BEFORE INSERT ON Domain FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); -CREATE OR REPLACE FUNCTION domainOwner(unixUserName varchar, domainName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION domainOwner(dom Domain) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('domain', unixUserName || '/' || domainName, 'owner'); + return roleDescriptor('domain', dom.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION domainAdmin(unixUserName varchar, domainName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION domainAdmin(dom Domain) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('domain', unixUserName || '/' || domainName, 'admin'); + return roleDescriptor('domain', dom.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION domainTenant(unixUserName varchar, domainName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION domainTenant(dom Domain) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('domain', unixUserName || '/' || domainName, 'tenant'); + return roleDescriptor('domain', dom.uuid, 'tenant'); end; $$; @@ -42,7 +45,8 @@ CREATE OR REPLACE FUNCTION createRbacRulesForDomain() RETURNS trigger LANGUAGE plpgsql STRICT AS $$ DECLARE - parentUser unixuser; + parentUser UnixUser; + parentPackage package; domainOwnerRoleUuid uuid; domainAdminRoleUuid uuid; BEGIN @@ -50,25 +54,26 @@ BEGIN RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; END IF; - SELECT * FROM unixuser WHERE uuid=NEW.unixUserUuid into parentUser; + SELECT * FROM UnixUser WHERE uuid=NEW.unixUserUuid into parentUser; + SELECT * FROM Package WHERE uuid=parentUser.packageuuid into parentPackage; -- a domain owner role is created and assigned to the unixuser's admin role domainOwnerRoleUuid = createRole( - domainOwner(parentUser.name, NEW.name), + domainOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(unixUserAdmin(parentUser.name)) + beneathRole(packageAdmin(parentPackage)) ); -- a domain admin role is created and assigned to the domain's owner role domainAdminRoleUuid = createRole( - domainAdmin(parentUser.name, NEW.name), + domainAdmin(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-emailaddress']), beneathRole(domainOwnerRoleUuid) ); -- and a domain tenant role is created and assigned to the domain's admiin role perform createRole( - domainTenant(parentUser.name, NEW.name), + domainTenant(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), beneathRole(domainAdminRoleUuid), beingItselfA(createUnixUserTenantRoleIfNotExists(parentUser)) diff --git a/sql/25-hs-emailaddress.sql b/sql/25-hs-emailaddress.sql index 7ff46b5a..12aba6ee 100644 --- a/sql/25-hs-emailaddress.sql +++ b/sql/25-hs-emailaddress.sql @@ -16,50 +16,51 @@ CREATE TRIGGER createRbacObjectForEMailAddress_Trigger BEFORE INSERT ON EMailAddress FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); -CREATE OR REPLACE FUNCTION emailAddressOwner(emailAddress varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION emailAddressOwner(emAddr EMailAddress) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('emailaddress', emailAddress, 'owner'); + return roleDescriptor('emailaddress', emAddr.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION emailAddressAdmin(emailAddress varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION emailAddressAdmin(emAddr EMailAddress) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('emailaddress', emailAddress, 'admin'); + return roleDescriptor('emailaddress', emAddr.uuid, 'admin'); end; $$; CREATE OR REPLACE FUNCTION createRbacRulesForEMailAddress() RETURNS trigger LANGUAGE plpgsql STRICT AS $$ DECLARE - parentDomain record; - eMailAddress varchar; + parentDomain Domain; eMailAddressOwnerRoleUuid uuid; BEGIN IF TG_OP <> 'INSERT' THEN RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; END IF; - SELECT d.name as name, u.name as unixUserName FROM domain d - LEFT JOIN unixuser u ON u.uuid = d.unixuseruuid - WHERE d.uuid=NEW.domainUuid into parentDomain; - eMailAddress = NEW.localPart || '@' || parentDomain.name; + SELECT d.* + FROM domain d + LEFT JOIN unixuser u ON u.uuid = d.unixuseruuid + WHERE d.uuid=NEW.domainUuid INTO parentDomain; -- an owner role is created and assigned to the domains's admin group eMailAddressOwnerRoleUuid = createRole( - emailAddressOwner(eMailAddress), + emailAddressOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(domainAdmin( parentDomain.unixUserName, parentDomain.name)) + beneathRole(domainAdmin( parentDomain)) ); -- and an admin role is created and assigned to the unixuser owner as well perform createRole( - emailAddressAdmin(eMailAddress), + emailAddressAdmin(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit']), beneathRole(eMailAddressOwnerRoleUuid), - beingItselfA(domainTenant(parentDomain.unixUserName, parentDomain.name)) + beingItselfA(domainTenant(parentDomain)) ); RETURN NEW; From 457641a2dda4967c359f68f4a459f65ed69d521f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 28 Jul 2022 10:43:23 +0200 Subject: [PATCH 13/54] introduce referential integrity for role identification - part 2 assume --- .run/30-run-all.sql.run.xml | 8 -------- sql/10-rbac-base.sql | 17 +++++++++++++++++ sql/21-hs-customer.sql | 17 ++++++++++++++++- sql/22-hs-packages.sql | 2 +- sql/23-hs-unixuser.sql | 2 +- sql/24-hs-domain.sql | 2 +- sql/25-hs-emailaddress.sql | 2 +- sql/{28--hs-tests.sql => 28-hs-tests.sql} | 4 ++-- sql/examples.sql | 9 --------- sql/{19--rbac-tests.sql => rbac-tests.sql} | 0 ...ons.sql => rbac-view-option-experiments.sql} | 0 11 files changed, 39 insertions(+), 24 deletions(-) delete mode 100644 .run/30-run-all.sql.run.xml rename sql/{28--hs-tests.sql => 28-hs-tests.sql} (98%) rename sql/{19--rbac-tests.sql => rbac-tests.sql} (100%) rename sql/{11--rbac-view-options.sql => rbac-view-option-experiments.sql} (100%) diff --git a/.run/30-run-all.sql.run.xml b/.run/30-run-all.sql.run.xml deleted file mode 100644 index e17e8564..00000000 --- a/.run/30-run-all.sql.run.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - FILE - - - - \ No newline at end of file diff --git a/sql/10-rbac-base.sql b/sql/10-rbac-base.sql index 68124563..06ba3442 100644 --- a/sql/10-rbac-base.sql +++ b/sql/10-rbac-base.sql @@ -670,6 +670,21 @@ BEGIN RETURN string_to_array(currentSubject, ';'); END; $$; +CREATE OR REPLACE FUNCTION findUuidByIdName(objectTable varchar, objectIdName varchar) + RETURNS uuid + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +DECLARE + +BEGIN + /*sql = 'E ' || baseTable || '_historicize' || + ' AFTER INSERT OR DELETE OR UPDATE ON ' || baseTable || + ' FOR EACH ROW EXECUTE PROCEDURE historicize()'; + RAISE NOTICE 'sql: %', createTriggerSQL; + EXECUTE createTriggerSQ*/ + + RETURN customerUuidByIdName(objectIdName); +END; $$; ROLLBACK; SET SESSION AUTHORIZATION DEFAULT; @@ -702,6 +717,8 @@ BEGIN objectNameToAssume = split_part(roleName, '#', 2); roleTypeToAssume = split_part(roleName, '#', 3); + objectUuidToAssume = findUuidByIdName(objectTableToAssume, objectNameToAssume); + -- TODO: either the result needs to be cached at least per transaction or we need to get rid of SELCT in a loop SELECT uuid AS roleuuidToAssume FROM RbacRole r diff --git a/sql/21-hs-customer.sql b/sql/21-hs-customer.sql index 3fcb2b1e..a386bc32 100644 --- a/sql/21-hs-customer.sql +++ b/sql/21-hs-customer.sql @@ -105,10 +105,25 @@ CREATE TRIGGER deleteRbacRulesForCustomer_Trigger BEFORE DELETE ON customer FOR EACH ROW EXECUTE PROCEDURE deleteRbacRulesForCustomer(); +-- create a restricted view to access the textual customer ids a idName +SET SESSION SESSION AUTHORIZATION DEFAULT; +-- ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +DROP VIEW IF EXISTS customer_iv; +CREATE OR REPLACE VIEW customer_iv AS +SELECT DISTINCT target.uuid, target.prefix as idName + FROM customer AS target; +-- TODO: Is it ok that everybody has access to this information? +GRANT ALL PRIVILEGES ON customer_iv TO restricted; + +CREATE OR REPLACE FUNCTION customerUuidByIdName(idName varchar) + RETURNS uuid + LANGUAGE sql STRICT AS $$ + SELECT uuid FROM customer_iv iv WHERE iv.idName=customerUuidByIdName.idName; + $$; -- create RBAC restricted view SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE customer ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS customer_rv; CREATE OR REPLACE VIEW customer_rv AS SELECT DISTINCT target.* diff --git a/sql/22-hs-packages.sql b/sql/22-hs-packages.sql index cd25bc53..65eed84a 100644 --- a/sql/22-hs-packages.sql +++ b/sql/22-hs-packages.sql @@ -105,7 +105,7 @@ CREATE TRIGGER deleteRbacRulesForPackage_Trigger -- create RBAC-restricted view SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE package ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE package ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS package_rv; CREATE OR REPLACE VIEW package_rv AS SELECT DISTINCT target.* diff --git a/sql/23-hs-unixuser.sql b/sql/23-hs-unixuser.sql index 52ac180c..00773135 100644 --- a/sql/23-hs-unixuser.sql +++ b/sql/23-hs-unixuser.sql @@ -107,7 +107,7 @@ CREATE TRIGGER createRbacRulesForUnixUser_Trigger -- create RBAC-restricted view SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS unixuser_rv; CREATE OR REPLACE VIEW unixuser_rv AS SELECT DISTINCT target.* diff --git a/sql/24-hs-domain.sql b/sql/24-hs-domain.sql index b8588913..5435b35b 100644 --- a/sql/24-hs-domain.sql +++ b/sql/24-hs-domain.sql @@ -92,7 +92,7 @@ CREATE TRIGGER createRbacRulesForDomain_Trigger -- create RBAC-restricted view SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS domain_rv; CREATE OR REPLACE VIEW domain_rv AS SELECT DISTINCT target.* diff --git a/sql/25-hs-emailaddress.sql b/sql/25-hs-emailaddress.sql index 12aba6ee..80cdb759 100644 --- a/sql/25-hs-emailaddress.sql +++ b/sql/25-hs-emailaddress.sql @@ -76,7 +76,7 @@ CREATE TRIGGER createRbacRulesForEMailAddress_Trigger -- create RBAC-restricted view SET SESSION SESSION AUTHORIZATION DEFAULT; -ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; DROP VIEW IF EXISTS EMailAddress_rv; CREATE OR REPLACE VIEW EMailAddress_rv AS SELECT DISTINCT target.* diff --git a/sql/28--hs-tests.sql b/sql/28-hs-tests.sql similarity index 98% rename from sql/28--hs-tests.sql rename to sql/28-hs-tests.sql index e52a2ca3..216147ca 100644 --- a/sql/28--hs-tests.sql +++ b/sql/28-hs-tests.sql @@ -61,7 +61,7 @@ BEGIN FROM unixuser_rv uu JOIN package_rv p ON p.uuid = uu.packageuuid JOIN customer_rv c ON c.uuid = p.customeruuid; - call expectBetween(resultCount, 30, 50); + call expectBetween(resultCount, 40, 60); -- hostsharing admin assuming two customer admin roles and listing all accessible domains -- ABORT; START TRANSACTION; @@ -74,7 +74,7 @@ BEGIN JOIN unixuser_rv uu ON uu.uuid = dom.unixuseruuid JOIN package_rv p ON p.uuid = uu.packageuuid JOIN customer_rv c ON c.uuid = p.customeruuid; - call expectBetween(resultCount, 30, 50); + call expectBetween(resultCount, 20, 40); -- hostsharing admin assuming two customer admin roles and listing all accessible email addresses -- ABORT; START TRANSACTION; diff --git a/sql/examples.sql b/sql/examples.sql index bcbfe2fc..13219654 100644 --- a/sql/examples.sql +++ b/sql/examples.sql @@ -46,15 +46,6 @@ SET LOCAL hsadminng.currentUser TO 'mih42_customer_aaa'; SET LOCAL hsadminng.currentTask TO 'adding customer_aaa'; INSERT INTO package (customer_id, name) VALUES (10000, 'aaa00'); COMMIT; - -SET SESSION SESSION AUTHORIZATION DEFAULT; -CREATE ROLE hs_sel_package_1000000; -GRANT hs_sel_package_1000000 to hs_sel_customer_10000; - -SET SESSION SESSION AUTHORIZATION mih42_customer_aaa; -SELECT pg_has_role('hs_sel_package_1000000', 'MEMBER'); - - -- Usage: SET hsadminng.timestamp TO '2022-07-12 08:53:27.723315'; diff --git a/sql/19--rbac-tests.sql b/sql/rbac-tests.sql similarity index 100% rename from sql/19--rbac-tests.sql rename to sql/rbac-tests.sql diff --git a/sql/11--rbac-view-options.sql b/sql/rbac-view-option-experiments.sql similarity index 100% rename from sql/11--rbac-view-options.sql rename to sql/rbac-view-option-experiments.sql From 306f8d1fa8c70719684db06ded41cc0453fc089b Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 28 Jul 2022 10:43:49 +0200 Subject: [PATCH 14/54] improved README.md with aliases for pg-sql-... --- .aliases | 8 ++++++++ README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 .aliases diff --git a/.aliases b/.aliases new file mode 100644 index 00000000..483931c5 --- /dev/null +++ b/.aliases @@ -0,0 +1,8 @@ +alias pg-sql-run='docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:13.7-bullseye' +alias pg-sql-stop='docker stop hsadmin-ng-postgres' +alias pg-sql-start='docker container start hsadmin-ng-postgres' +alias pg-sql-remove='docker rm hsadmin-ng-postgres' +alias pg-sql-reset='pg-sql-stop; pg-sql-remove; pg-sql-run' +alias pg-sql-backup='docker exec -i hsadmin-ng-postgres /usr/bin/pg_dump --clean --create -U postgres postgres | gzip -9' +alias pg-sql-restore='gunzip --stdout | docker exec -i hsadmin-ng-postgres psql -U postgres -d postgres' + diff --git a/README.md b/README.md index 227a0eb6..cfcafc09 100644 --- a/README.md +++ b/README.md @@ -6,43 +6,69 @@ So far the spike contains almost only PostgreSQL Code. All you need so far, is a PostgreSQL database, for now with full admin rights. -The easiest way to set it up is using docker: +The easiest way to set it up is using docker. + +(Find the mentioned aliases in `.aliases`.) Initially, pull an image compatible to current PostgreSQL version of Hostsharing: docker pull postgres:13.7-bullseye +**⚠** +If we switch the version, please also amend the documentation as well as the aliases file. Thanks! + Create and run a container with the given PostgreSQL version: docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:13.7-bullseye + # or via alias: + pg-sql-run + To check if the PostgreSQL container is running, the following command should list a container with the name "hsadmin-ng-postgres": - docker container ls + docker container ls Stop the PostgreSQL container: docker stop hsadmin-ng-postgres + # or via alias: pg-sql-stop Start the PostgreSQL container again: docker container start hsadmin-ng-postgres + # or via alias: pg-sql-start Remove the PostgreSQL container: docker rm hsadmin-ng-postgres + + # or via alias: + pg-sql-remove + +To reset to a clean database, use: + + pg-sql-stop; pg-sql-remove; pg-sql-run + + # or via alias: + pg-sql-reset After the PostgreSQL container is removed, you need to create it again as shown in "Create and run ..." above. Given the container is running, to create a backup in ~/backup, run: - docker exec -i hsadmin-ng-postgres /usr/bin/pg_dump --clean --create -U postgres postgres | gzip -9 > ~/backup/hsadmin-ng-postgres2.sql.gz + docker exec -i hsadmin-ng-postgres /usr/bin/pg_dump --clean --create -U postgres postgres | gzip -9 > ~/backup/hsadmin-ng-postgres.sql.gz + + # or via alias: + pg-sql-backup >~/backup/hsadmin-ng-postgres.sql.gz Again, given the container is running, to restore the backup from ~/backup, run: gunzip --stdout --keep ~/backup/hsadmin-ng-postgres.sql.gz | docker exec -i hsadmin-ng-postgres psql -U postgres -d postgres + # or via alias: + pg-sql-restore <~/backup/hsadmin-ng-postgres.sql.gz + ### Markdown with PlantUML plugin @@ -72,7 +98,7 @@ sudo apt install graphviz ``` -### Ubuntu Linux command line +#### Ubuntu Linux command line ```sh sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive-extra-utils texlive-latex-extra pandoc-plantuml-filter @@ -82,6 +108,26 @@ sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive pandoc --filter pandoc-plantuml rbac.md -o rbac.pdf ``` -### for other IDEs / operating systems +#### for other IDEs / operating systems If you have figured out how it works, please add instructions above this section. + + +## Running the SQL files + +### For RBAC + +If you run the numbered SQL files from the `sql` folder in the defined order, a working RBAC system is built up in the database including test data and some simple tests. + +To increase the amount of test data, simply increase the number of generated customers in `21-hs-customer.sql`. + +If you already have data, e.g. for customers 0..999 (thus with reference numbers 10000..10999) and want to add another 1000 customers, amend the for loop to 1000...1999 and also uncomment and amend the `CONTINUE WHEN` or `WHERE` conditions in the other test data generators, using the first new customer reference number (in the example that's 11000). + +### For Historization + +You can explore the historization prototype as follows: + +- start with an empty database + (the example tables are currently not compatible with RBAC), +- then run `historization.sql,` +- finally run `examples.sql`. From feff1b5794180cdd7504eb65fc7fcd419b757bff Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 28 Jul 2022 12:15:32 +0200 Subject: [PATCH 15/54] RBAC documentation improved, but still WIP --- sql/rbac.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/sql/rbac.md b/sql/rbac.md index a7abd39d..83a02241 100644 --- a/sql/rbac.md +++ b/sql/rbac.md @@ -553,3 +553,83 @@ Initially, the customer's admin role is assigned to the package owner role. They can use the package's admin role to hand over most management functionality to a third party. The 'administrators' can get access through an assumed customer's admin role or directly by assuming the package's owner or admin role. +## Performance + +We did not define maximum response time in our requirements, +but set a target of 7.000 customers, 15.000 packages, 150.000 Unix users, 100.000 domains and 500.000 email-addresses. + +For such a dataset the response time for typical queries from a UI should be acceptable. +Also, when adding data beyond these quantities, increase in response time should be roughly linear or below. +For this, we increased the dataset by 14% and then by another 25%, ending up with 10.000 customers, almost 25.000 packages, over 174.000 unix users, over 120.000 domains and almost 750.000 email-addresses. + +The performance test suite comprised 8 SELECT queries issued by an administrator, mostly with two assumed customer owner roles. +The tests started with finding a specific customer and ended with listing all accessible email-addresses joined with their domains, unix-users, packages and customers. + +Find the SQL script here: `28-hs-tests.sql`. + +### Two View Query Variants + +We have tested two variants of the query for the restricted view, +both utilizing a PostgreSQL function like this: + + FUNCTION queryAccessibleObjectUuidsOfSubjectIds( + requiredOp RbacOp, + forObjectTable varchar, + subjectIds uuid[], + maxObjects integer = 16000) + RETURNS SETOF uuid + +The function returns all object uuids for which the given subjectIds (user o assumed roles) have a permission or required operation. + +Let's have a look at the two view queries: + +#### Using WHERE ... IN + + CREATE OR REPLACE VIEW customer_rv AS + SELECT DISTINCT target.* + FROM customer AS target + WHERE target.uuid IN ( + SELECT uuid + FROM queryAccessibleObjectUuidsOfSubjectIds( + 'view', 'customer', currentSubjectIds())); + +This view should be automatically updatable. +Where, for updates, we actually have to check for 'edit' instead of 'view' operation, which makes it a bit more complicated. + +With the larger dataset, the test suite initially needed over 7 seconds with this view query. +At this point the second variant was tried. + +But after the initial query, the execution time was drastically reduced, +even with different query values. +Looks like the query optimizer needed some statistics to find the best path. + +#### Using A JOIN + + CREATE OR REPLACE VIEW customer_rv AS + SELECT DISTINCT target.* + FROM customer AS target + JOIN queryAccessibleObjectUuidsOfSubjectIds( + 'view', 'customer', currentSubjectIds()) AS allowedObjId + ON target.uuid = allowedObjId; + +This view cannot is not updatable automatically, +but it was quite fast from the beginning. + +### Performance Results + +The following table shows the average between the second and the third repeat of the test-suite: + +| Dataset | using JOIN | using WHERE IN | +|----------------:|-----------:|---------------:| +| 7000 customers | 670ms | 1040ms | +| 10000 customers | 1050ms | 1125ms | +| +43% | +57% | +8% | + +The JOIN-variant is still faster, but the growth in execution time exceeded the growth of the dataset. + +The WHERE-IN-variant is about 50% slower on the smaller dataset, but almost keeps its performance on the larger dataset. + +Both variants a viable option, depending on other needs, e.g. updatable views. + + + From 6c33bbe780098dc23c9a49dcf5753d705a0227d6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 28 Jul 2022 16:51:36 +0200 Subject: [PATCH 16/54] add SpringBoot 2.7.x application --- .aliases | 1 + .gitignore | 176 +++---------- LICENSE.md | 26 ++ build.gradle | 56 ++++ gradlew | 240 ++++++++++++++++++ gradlew.bat | 91 +++++++ settings.gradle | 1 + .../hsadminng/HsadminNgApplication.java | 13 + .../hostsharing/hsadminng/TestController.java | 37 +++ src/main/resources/application.yml | 11 + 10 files changed, 510 insertions(+), 142 deletions(-) create mode 100644 LICENSE.md create mode 100644 build.gradle create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/net/hostsharing/hsadminng/HsadminNgApplication.java create mode 100644 src/main/java/net/hostsharing/hsadminng/TestController.java create mode 100644 src/main/resources/application.yml diff --git a/.aliases b/.aliases index 483931c5..03428b86 100644 --- a/.aliases +++ b/.aliases @@ -1,3 +1,4 @@ +alias gw='./gradlew' alias pg-sql-run='docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:13.7-bullseye' alias pg-sql-stop='docker stop hsadmin-ng-postgres' alias pg-sql-start='docker container start hsadmin-ng-postgres' diff --git a/.gitignore b/.gitignore index 74c375c4..c2065bc2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,145 +1,37 @@ -###################### -# Project Specific -###################### -/build/www/** -/src/test/javascript/coverage/ -/worktrees/ - -###################### -# Node -###################### -/node/ -node_tmp/ -node_modules/ -npm-debug.log.* -/.awcache/* -/.cache-loader/* - -###################### -# SASS -###################### -.sass-cache/ - -###################### -# Eclipse -###################### -*.pydevproject -.project -.metadata -tmp/ -tmp/**/* -*.tmp -*.bak -*.swp -*~.nib -local.properties -.classpath -.settings/ -.loadpath -.factorypath -/src/main/resources/rebel.xml - -# External tool builders -.externalToolBuilders/** - -# Locally stored "Eclipse launch configurations" -*.launch - -# CDT-specific -.cproject - -# PDT-specific -.buildpath - -###################### -# Intellij -###################### -.idea/ -*.iml -*.iws -*.ipr -*.ids -*.orig -classes/ -out/ - -###################### -# Visual Studio Code -###################### -.vscode/ - -###################### -# Maven -###################### -/log/ -/target/ - -###################### -# Gradle -###################### -.gradle/ -/build/ - -###################### -# Package Files -###################### -*.jar -*.war -*.ear -*.db - -###################### -# Windows -###################### -# Windows image file caches -Thumbs.db - -# Folder config file -Desktop.ini - -###################### -# Mac OSX -###################### -.DS_Store -.svn - -# Thumbnails -._* - -# Files that might appear on external disk -.Spotlight-V100 -.Trashes - -###################### -# Directories -###################### -/bin/ -/deploy/ - -###################### -# Logs -###################### -*.log* - -###################### -# Others -###################### -*.class -*.*~ -*~ -.merge_file* - -###################### -# Gradle Wrapper -###################### +HELP.md +.gradle +build/ !gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ -###################### -# Maven Wrapper -###################### -!.mvn/wrapper/maven-wrapper.jar +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ -###################### -# ESLint -###################### -.eslintcache +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..a7ab6be3 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,26 @@ +The MIT License (MIT) +===================== + +Copyright ©2022 Michael Hönnig + +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/build.gradle b/build.gradle new file mode 100644 index 00000000..6f6c6dc2 --- /dev/null +++ b/build.gradle @@ -0,0 +1,56 @@ +plugins { + id 'org.springframework.boot' version '2.7.2' + id 'io.spring.dependency-management' version '1.0.12.RELEASE' + id 'java' +} + +group = 'net.hostsharing' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '17' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +ext { + set('testcontainersVersion', "1.17.3") +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-data-rest' + implementation 'org.springframework.boot:spring-boot-starter-hateoas' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.liquibase:liquibase-core' + implementation 'org.springframework.data:spring-data-rest-hal-explorer' + + compileOnly 'org.projectlombok:lombok' + + developmentOnly 'org.springframework.boot:spring-boot-devtools' + + runtimeOnly 'org.postgresql:postgresql' + + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'org.testcontainers:junit-jupiter' + testImplementation 'org.testcontainers:postgresql' +} + +dependencyManagement { + imports { + mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}" + } +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..a69d9cb6 --- /dev/null +++ b/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..f127cfd4 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..285ecb0c --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'hsadmin-ng' diff --git a/src/main/java/net/hostsharing/hsadminng/HsadminNgApplication.java b/src/main/java/net/hostsharing/hsadminng/HsadminNgApplication.java new file mode 100644 index 00000000..20764d4e --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/HsadminNgApplication.java @@ -0,0 +1,13 @@ +package net.hostsharing.hsadminng; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class HsadminNgApplication { + + public static void main(String[] args) { + SpringApplication.run(HsadminNgApplication.class, args); + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/TestController.java b/src/main/java/net/hostsharing/hsadminng/TestController.java new file mode 100644 index 00000000..cddadf00 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/TestController.java @@ -0,0 +1,37 @@ +package net.hostsharing.hsadminng; + +import org.hibernate.type.PostgresUUIDType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; +import java.util.List; + +@Controller +public class TestController { + + @PersistenceContext + private EntityManager em; + + @RequestMapping(value = "/api/test", method = RequestMethod.GET) + public List test() { + final var query = em.createNativeQuery("select * from public.rbacuser") + .unwrap(org.hibernate.query.NativeQuery.class) + .addScalar("uuidColumn", PostgresUUIDType.INSTANCE); + return query.getResultList(); + } + + @Transactional + @ResponseBody + @RequestMapping(value = "/api/currentUser", method = RequestMethod.GET) + public String currentUser() { + em.createNativeQuery("SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';").executeUpdate(); + em.createNativeQuery("SET LOCAL hsadminng.assumedRoles = '';").executeUpdate(); + final var query = em.createNativeQuery("select currentUser()"); + return query.getSingleResult().toString(); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 00000000..a1fead7f --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,11 @@ +spring: + + datasource: + driver-class-name: org.postgresql.Driver + password: password + url: jdbc:postgresql://localhost:5432/postgres + username: postgres + + sql: + init: + mode: never From d234ac32271e5bce3f0f38e62fc2e9f4964e48d3 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 28 Jul 2022 16:55:21 +0200 Subject: [PATCH 17/54] convert rbac*.sql files, except test-file, to Liquibase changesets --- sql/00-util.sql | 63 -- sql/28-hs-tests.sql | 9 + .../db/changelog}/12-rbac-role-builder.sql | 0 .../db/changelog}/18-rbac-statistics.sql | 0 .../resources/db/changelog}/20-hs-base.sql | 0 .../db/changelog/2022-07-28-000-template.sql | 9 + .../2022-07-28-001-last-row-count.sql | 18 + .../changelog/2022-07-28-002-int-to-var.sql | 25 + .../2022-07-28-003-random-in-range.sql | 23 + .../2022-07-28-004-uuid-ossp-extension.sql | 9 + .../db/changelog/2022-07-28-005-rbac-base.sql | 548 ++++++++---------- .../db/changelog}/21-hs-customer.sql | 0 .../db/changelog}/22-hs-packages.sql | 0 .../db/changelog}/23-hs-unixuser.sql | 0 .../resources/db/changelog}/24-hs-domain.sql | 0 .../db/changelog}/25-hs-emailaddress.sql | 0 .../db/changelog}/29-hs-statistics.sql | 0 .../db/changelog/db.changelog-master.yaml | 12 + 18 files changed, 345 insertions(+), 371 deletions(-) delete mode 100644 sql/00-util.sql rename {sql => src/main/resources/db/changelog}/12-rbac-role-builder.sql (100%) rename {sql => src/main/resources/db/changelog}/18-rbac-statistics.sql (100%) rename {sql => src/main/resources/db/changelog}/20-hs-base.sql (100%) create mode 100644 src/main/resources/db/changelog/2022-07-28-000-template.sql create mode 100644 src/main/resources/db/changelog/2022-07-28-001-last-row-count.sql create mode 100644 src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql create mode 100644 src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql create mode 100644 src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql rename sql/10-rbac-base.sql => src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql (71%) rename {sql => src/main/resources/db/changelog}/21-hs-customer.sql (100%) rename {sql => src/main/resources/db/changelog}/22-hs-packages.sql (100%) rename {sql => src/main/resources/db/changelog}/23-hs-unixuser.sql (100%) rename {sql => src/main/resources/db/changelog}/24-hs-domain.sql (100%) rename {sql => src/main/resources/db/changelog}/25-hs-emailaddress.sql (100%) rename {sql => src/main/resources/db/changelog}/29-hs-statistics.sql (100%) create mode 100644 src/main/resources/db/changelog/db.changelog-master.yaml diff --git a/sql/00-util.sql b/sql/00-util.sql deleted file mode 100644 index 7f39452e..00000000 --- a/sql/00-util.sql +++ /dev/null @@ -1,63 +0,0 @@ -abort; -set local session authorization default; - - -CREATE OR REPLACE FUNCTION array_distinct(anyarray) RETURNS anyarray AS $f$ -SELECT array_agg(DISTINCT x) FROM unnest($1) t(x); -$f$ LANGUAGE SQL IMMUTABLE; - - -CREATE OR REPLACE FUNCTION lastRowCount() - RETURNS bigint - LANGUAGE plpgsql AS $$ -DECLARE - lastRowCount bigint; -BEGIN - GET DIAGNOSTICS lastRowCount = ROW_COUNT; - RETURN lastRowCount; -END; -$$; - --- ======================================================== --- Test Data helpers --- -------------------------------------------------------- - -CREATE OR REPLACE FUNCTION intToVarChar(i integer, len integer) - RETURNS varchar - LANGUAGE plpgsql AS $$ -DECLARE -partial varchar; -BEGIN -SELECT chr(ascii('a') + i%26) INTO partial; -IF len > 1 THEN - RETURN intToVarChar(i/26, len-1) || partial; -ELSE - RETURN partial; -END IF; -END; $$; - -select * from intToVarChar(211, 4); - -CREATE OR REPLACE FUNCTION randomInRange(min INTEGER, max INTEGER) - RETURNS INT - RETURNS NULL ON NULL INPUT - language 'plpgsql' AS $$ -BEGIN - RETURN floor(random() * (max-min + 1) + min); -END; $$; - -select * from randomInRange(0, 4); - - --- ======================================================== --- Test helpers --- -------------------------------------------------------- - --- there are some random ractors in test data generation, thus a range has to be accepted -CREATE OR REPLACE PROCEDURE expectBetween(actualCount integer, expectedFrom integer, expectedTo integer) - LANGUAGE plpgsql AS $$ -BEGIN - IF NOT actualCount BETWEEN expectedFrom AND expectedTo THEN - RAISE EXCEPTION 'count expected to be between % and %, but got %', expectedFrom, expectedTo, actualCount; - END IF; -END; $$; diff --git a/sql/28-hs-tests.sql b/sql/28-hs-tests.sql index 216147ca..c7593ab3 100644 --- a/sql/28-hs-tests.sql +++ b/sql/28-hs-tests.sql @@ -1,6 +1,15 @@ ABORT; SET SESSION SESSION AUTHORIZATION DEFAULT; +-- there are some random ractors in test data generation, thus a range has to be accepted +CREATE OR REPLACE PROCEDURE expectBetween(actualCount integer, expectedFrom integer, expectedTo integer) + LANGUAGE plpgsql AS $$ +BEGIN + IF NOT actualCount BETWEEN expectedFrom AND expectedTo THEN + RAISE EXCEPTION 'count expected to be between % and %, but got %', expectedFrom, expectedTo, actualCount; +END IF; +END; $$; + DO LANGUAGE plpgsql $$ DECLARE resultCount integer; diff --git a/sql/12-rbac-role-builder.sql b/src/main/resources/db/changelog/12-rbac-role-builder.sql similarity index 100% rename from sql/12-rbac-role-builder.sql rename to src/main/resources/db/changelog/12-rbac-role-builder.sql diff --git a/sql/18-rbac-statistics.sql b/src/main/resources/db/changelog/18-rbac-statistics.sql similarity index 100% rename from sql/18-rbac-statistics.sql rename to src/main/resources/db/changelog/18-rbac-statistics.sql diff --git a/sql/20-hs-base.sql b/src/main/resources/db/changelog/20-hs-base.sql similarity index 100% rename from sql/20-hs-base.sql rename to src/main/resources/db/changelog/20-hs-base.sql diff --git a/src/main/resources/db/changelog/2022-07-28-000-template.sql b/src/main/resources/db/changelog/2022-07-28-000-template.sql new file mode 100644 index 00000000..04fe08fa --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-28-000-template.sql @@ -0,0 +1,9 @@ +--liquibase formatted sql + +--changeset template:1 endDelimiter:--// + +/* + + */ + +--// diff --git a/src/main/resources/db/changelog/2022-07-28-001-last-row-count.sql b/src/main/resources/db/changelog/2022-07-28-001-last-row-count.sql new file mode 100644 index 00000000..f6ca062f --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-28-001-last-row-count.sql @@ -0,0 +1,18 @@ +--liquibase formatted sql + +--changeset last-row-count:1 endDelimiter:--// + +/* + Returns the row count from the result of the previous query. + Other than the native statement it's usable in an expression. + */ +create or replace function lastRowCount() + returns bigint + language plpgsql as $$ +declare + lastRowCount bigint; +begin + get diagnostics lastrowCount = row_count; + return lastRowCount; +end; $$; +--// diff --git a/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql b/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql new file mode 100644 index 00000000..7ef0efce --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql @@ -0,0 +1,25 @@ +--liquibase formatted sql + +--changeset int-to-var:1 endDelimiter:--// + +/* + Returns a textual representation of an integer number to be used as generated test data. + + Examples : + intToVarChar(0, 3) => 'aaa' + intToVarChar(1, 3) => 'aab' + */ +create or replace function intToVarChar(i integer, len integer) + returns varchar + language plpgsql as $$ +declare + partial varchar; +begin + select chr(ascii('a') + i%26) into partial; + if len > 1 then + return intToVarChar(i/26, len-1) || partial; + else + return partial; + end if; +END; $$; +--// diff --git a/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql b/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql new file mode 100644 index 00000000..277984fc --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql @@ -0,0 +1,23 @@ +--liquibase formatted sql + +--changeset random-in-range:1 endDelimiter:--// + +/* + Returns a random integer in the given range (both included), + to be used for test data generation. + + Example: + randomInRange(0, 4) might return any of 0, 1, 2, 3, 4 + */ +create or replace function randomInRange(min integer, max integer) + returns integer + returns null on null input + language 'plpgsql' AS $$ +begin + return floor(random() * (max-min + 1) + min); +end; $$; +--// + + + + diff --git a/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql b/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql new file mode 100644 index 00000000..675b6ddb --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql @@ -0,0 +1,9 @@ +--liquibase formatted sql + +--changeset uuid-ossp-extension:1 endDelimiter:--// + +/* + Makes improved uuid generation available. + */ +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +--// diff --git a/sql/10-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql similarity index 71% rename from sql/10-rbac-base.sql rename to src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index 06ba3442..e879852f 100644 --- a/sql/10-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -7,14 +7,12 @@ SET SESSION SESSION AUTHORIZATION DEFAULT; -- https://arctype.com/blog/postgres-uuid/#creating-a-uuid-primary-key-using-uuid-osp-postgresql-example CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +--liquibase formatted sql -DROP TABLE IF EXISTS "RbacPermission"; -DROP TABLE IF EXISTS "RbacGrants"; -DROP TABLE IF EXISTS "RbacUser"; -DROP TABLE IF EXISTS RbacReference CASCADE; -DROP TYPE IF EXISTS RbacOp CASCADE; -DROP TYPE IF EXISTS ReferenceType CASCADE; +--changeset rbac-base-reference:1 endDelimiter:--// +/* + */ CREATE TYPE ReferenceType AS ENUM ('RbacUser', 'RbacRole', 'RbacPermission'); CREATE TABLE RbacReference @@ -23,88 +21,31 @@ CREATE TABLE RbacReference type ReferenceType not null ); +CREATE OR REPLACE FUNCTION assertReferenceType(argument varchar, referenceId uuid, expectedType ReferenceType) + RETURNS ReferenceType + LANGUAGE plpgsql AS $$ +DECLARE + actualType ReferenceType; +BEGIN + actualType = (SELECT type FROM RbacReference WHERE uuid=referenceId); + IF ( actualType <> expectedType ) THEN + RAISE EXCEPTION '% must reference a %, but got a %', argument, expectedType, actualType; + end if; + RETURN expectedType; +END; $$; + +--// + +--changeset rbac-base-user:1 endDelimiter:--// +/* + + */ CREATE TABLE RbacUser ( uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, name varchar(63) not null unique ); --- DROP TABLE IF EXISTS RbacObject; -CREATE TABLE RbacObject -( - uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4(), - objectTable varchar(64) not null, - unique (objectTable, uuid) -); - -CREATE TYPE RbacRoleType AS ENUM ('owner', 'admin', 'tenant'); - -CREATE TABLE RbacRole -( - uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, - objectUuid uuid references RbacObject(uuid) not null, - roleType RbacRoleType not null -); - -CREATE TABLE RbacGrants -( - ascendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, - descendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, - follow boolean not null default true, - primary key (ascendantUuid, descendantUuid) -); -CREATE INDEX ON RbacGrants (ascendantUuid); -CREATE INDEX ON RbacGrants (descendantUuid); - --- DROP DOMAIN IF EXISTS RbacOp CASCADE; -CREATE DOMAIN RbacOp AS VARCHAR(67) - CHECK( - VALUE = '*' - OR VALUE = 'delete' - OR VALUE = 'edit' - OR VALUE = 'view' - OR VALUE = 'assume' - OR VALUE ~ '^add-[a-z]+$' - ); - -CREATE OR REPLACE FUNCTION createRbacObject() - RETURNS trigger - LANGUAGE plpgsql STRICT AS $$ -DECLARE - objectUuid uuid; -BEGIN - IF TG_OP = 'INSERT' THEN - INSERT INTO RbacObject (objectTable) VALUES (TG_TABLE_NAME) RETURNING uuid INTO objectUuid; - NEW.uuid = objectUuid; - RETURN NEW; - ELSE - RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; - END IF; -END; $$; - - --- DROP TABLE IF EXISTS RbacPermission; -CREATE TABLE RbacPermission -( uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, - objectUuid uuid not null references RbacObject, - op RbacOp not null, - unique (objectUuid, op) -); - --- SET SESSION SESSION AUTHORIZATION DEFAULT; --- alter table rbacpermission add constraint rbacpermission_objectuuid_fkey foreign key (objectUuid) references rbacobject(uuid); --- alter table rbacpermission drop constraint rbacpermission_objectuuid; - -CREATE OR REPLACE FUNCTION hasPermission(forObjectUuid uuid, forOp RbacOp) - RETURNS bool - LANGUAGE sql AS $$ - SELECT EXISTS ( - SELECT op - FROM RbacPermission p - WHERE p.objectUuid=forObjectUuid AND p.op in ('*', forOp) - ); - $$; - CREATE OR REPLACE FUNCTION createRbacUser(userName varchar) RETURNS uuid RETURNS NULL ON NULL INPUT @@ -122,7 +63,7 @@ CREATE OR REPLACE FUNCTION findRbacUserId(userName varchar) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE sql AS $$ - SELECT uuid FROM RbacUser WHERE name = userName +SELECT uuid FROM RbacUser WHERE name = userName $$; CREATE TYPE RbacWhenNotExists AS ENUM ('fail', 'create'); @@ -147,46 +88,65 @@ BEGIN END; $$; -CREATE TYPE RbacRoleDescriptor AS - ( - objectTable varchar(63), -- TODO: needed? remove? - objectUuid uuid, - roleType RbacRoleType - ); +--// --- TODO: this ... -CREATE OR REPLACE FUNCTION roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType ) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - STABLE LEAKPROOF - LANGUAGE plpgsql AS $$ -BEGIN - RETURN ROW(objectTable, objectUuid, roleType); -END; $$; +--changeset rbac-base-object:1 endDelimiter:--// +/* -CREATE FUNCTION new_emp() RETURNS emp AS $$ -SELECT text 'None' AS name, - 1000.0 AS salary, - 25 AS age, - point '(2,2)' AS cubicle; -$$ LANGUAGE SQL; + */ +CREATE TABLE RbacObject +( + uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4(), + objectTable varchar(64) not null, + unique (objectTable, uuid) +); -DO LANGUAGE plpgsql $$ +CREATE OR REPLACE FUNCTION createRbacObject() + RETURNS trigger + LANGUAGE plpgsql STRICT AS $$ DECLARE - roleDesc RbacRoleDescriptor; + objectUuid uuid; BEGIN - select * FROM roleDescriptor('global', gen_random_uuid(), 'admin') into roleDesc; - RAISE NOTICE 'result: % % %', roleDesc.objecttable, roleDesc.objectuuid, roleDesc.roletype; + IF TG_OP = 'INSERT' THEN + INSERT INTO RbacObject (objectTable) VALUES (TG_TABLE_NAME) RETURNING uuid INTO objectUuid; + NEW.uuid = objectUuid; + RETURN NEW; + ELSE + RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; + END IF; END; $$; --- TODO: ... or that? + +--// + +--changeset rbac-base-role:1 endDelimiter:--// +/* + + */ + +CREATE TYPE RbacRoleType AS ENUM ('owner', 'admin', 'tenant'); + +CREATE TABLE RbacRole +( + uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, + objectUuid uuid references RbacObject(uuid) not null, + roleType RbacRoleType not null +); + +CREATE TYPE RbacRoleDescriptor AS +( + objectTable varchar(63), -- TODO: needed? remove? + objectUuid uuid, + roleType RbacRoleType +); + CREATE OR REPLACE FUNCTION roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType ) RETURNS RbacRoleDescriptor RETURNS NULL ON NULL INPUT -- STABLE LEAKPROOF LANGUAGE sql AS $$ - SELECT objectTable, objectUuid, roleType::RbacRoleType; - $$; +SELECT objectTable, objectUuid, roleType::RbacRoleType; +$$; @@ -216,7 +176,7 @@ CREATE OR REPLACE FUNCTION findRoleId(roleDescriptor RbacRoleDescriptor) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE sql AS $$ - SELECT uuid FROM RbacRole WHERE objectUuid = roleDescriptor.objectUuid AND roleType = roleDescriptor.roleType; +SELECT uuid FROM RbacRole WHERE objectUuid = roleDescriptor.objectUuid AND roleType = roleDescriptor.roleType; $$; CREATE OR REPLACE FUNCTION getRoleId(roleDescriptor RbacRoleDescriptor, whenNotExists RbacWhenNotExists) @@ -239,7 +199,41 @@ BEGIN END; $$; --- select getRoleId('hostmaster', 'create'); +--changeset rbac-base-permission:1 endDelimiter:--// +/* + + */ +CREATE DOMAIN RbacOp AS VARCHAR(67) + CHECK( + VALUE = '*' + OR VALUE = 'delete' + OR VALUE = 'edit' + OR VALUE = 'view' + OR VALUE = 'assume' + OR VALUE ~ '^add-[a-z]+$' + ); + +-- DROP TABLE IF EXISTS RbacPermission; +CREATE TABLE RbacPermission +( uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, + objectUuid uuid not null references RbacObject, + op RbacOp not null, + unique (objectUuid, op) +); + +-- SET SESSION SESSION AUTHORIZATION DEFAULT; +-- alter table rbacpermission add constraint rbacpermission_objectuuid_fkey foreign key (objectUuid) references rbacobject(uuid); +-- alter table rbacpermission drop constraint rbacpermission_objectuuid; + +CREATE OR REPLACE FUNCTION hasPermission(forObjectUuid uuid, forOp RbacOp) + RETURNS bool + LANGUAGE sql AS $$ + SELECT EXISTS ( + SELECT op + FROM RbacPermission p + WHERE p.objectUuid=forObjectUuid AND p.op in ('*', forOp) + ); + $$; CREATE OR REPLACE FUNCTION createPermissions(forObjectUuid uuid, permitOps RbacOp[]) RETURNS uuid[] @@ -281,18 +275,103 @@ CREATE OR REPLACE FUNCTION findPermissionId(forObjectUuid uuid, forOp RbacOp) WHERE p.objectUuid=forObjectUuid AND p.op in ('*', forOp) $$; -CREATE OR REPLACE FUNCTION assertReferenceType(argument varchar, referenceId uuid, expectedType ReferenceType) - RETURNS ReferenceType - LANGUAGE plpgsql AS $$ -DECLARE - actualType ReferenceType; -BEGIN - actualType = (SELECT type FROM RbacReference WHERE uuid=referenceId); - IF ( actualType <> expectedType ) THEN - RAISE EXCEPTION '% must reference a %, but got a %', argument, expectedType, actualType; - end if; - RETURN expectedType; -END; $$; +--// + +--changeset rbac-base-grants:1 endDelimiter:--// +/* + + */ +CREATE TABLE RbacGrants +( + ascendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, + descendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, + follow boolean not null default true, + primary key (ascendantUuid, descendantUuid) +); +CREATE INDEX ON RbacGrants (ascendantUuid); +CREATE INDEX ON RbacGrants (descendantUuid); + + +--// + +CREATE OR REPLACE FUNCTION findGrantees(grantedId uuid) + RETURNS SETOF RbacReference + RETURNS NULL ON NULL INPUT + LANGUAGE sql AS $$ +SELECT reference.* +FROM ( + WITH RECURSIVE grants AS ( + SELECT + descendantUuid, + ascendantUuid + FROM + RbacGrants + WHERE + descendantUuid = grantedId + UNION ALL + SELECT + "grant".descendantUuid, + "grant".ascendantUuid + FROM + RbacGrants "grant" + INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid + ) SELECT + ascendantUuid + FROM + grants + ) as grantee + JOIN RbacReference reference ON reference.uuid=grantee.ascendantUuid; +$$; + +CREATE OR REPLACE FUNCTION isGranted(granteeId uuid, grantedId uuid) + RETURNS bool + RETURNS NULL ON NULL INPUT + LANGUAGE sql AS $$ +SELECT granteeId=grantedId OR granteeId IN ( + WITH RECURSIVE grants AS ( + SELECT descendantUuid, ascendantUuid + FROM RbacGrants + WHERE descendantUuid = grantedId + UNION ALL + SELECT "grant".descendantUuid, "grant".ascendantUuid + FROM RbacGrants "grant" + INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid + ) SELECT + ascendantUuid + FROM + grants +); +$$; + +CREATE OR REPLACE FUNCTION isPermissionGrantedToSubject(permissionId uuid, subjectId uuid) + RETURNS BOOL + STABLE LEAKPROOF + LANGUAGE sql AS $$ +SELECT EXISTS ( + SELECT * FROM RbacUser WHERE uuid IN ( + WITH RECURSIVE grants AS ( + SELECT + descendantUuid, + ascendantUuid + FROM + RbacGrants g + WHERE + g.descendantUuid = permissionId + UNION ALL + SELECT + g.descendantUuid, + g.ascendantUuid + FROM + RbacGrants g + INNER JOIN grants recur ON recur.ascendantUuid = g.descendantUuid + ) SELECT + ascendantUuid + FROM + grants + WHERE ascendantUuid=subjectId + ) + ); +$$; CREATE OR REPLACE PROCEDURE grantPermissionsToRole(roleUuid uuid, permissionIds uuid[]) LANGUAGE plpgsql AS $$ @@ -344,10 +423,12 @@ BEGIN INSERT INTO RbacGrants (ascendantUuid, descendantUuid) VALUES (userId, roleId) ON CONFLICT DO NOTHING ; -- TODO: remove? END; $$; +--// -abort; -set local session authorization default; +--changeset rbac-base-query-accessible-object-uuids:1 endDelimiter:--// +/* + */ CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( requiredOp RbacOp, forObjectTable varchar, -- reduces the result set, but is not really faster when used in restricted view @@ -388,27 +469,13 @@ CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( END; $$; -SET SESSION AUTHORIZATION DEFAULT; -CREATE ROLE admin; -GRANT USAGE ON SCHEMA public TO admin; -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin; -CREATE ROLE restricted; -GRANT USAGE ON SCHEMA public TO restricted; -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; +--// -abort; -set local session authorization restricted; -begin transaction; -set local statement_timeout TO '15s'; -select count(*) - from queryAccessibleObjectUuidsOfSubjectIds('view', 'customer', ARRAY[findRbacUserId('mike@hostsharing.net')], 10000); -end transaction; +--changeset rbac-base-query-granted-permissions:1 endDelimiter:--// +/* ---- - -abort; -set local session authorization default; -CREATE OR REPLACE FUNCTION queryRequiredPermissionsOfSubjectIds(requiredOp RbacOp, subjectIds uuid[]) + */ +CREATE OR REPLACE FUNCTION queryGrantedPermissionsOfSubjectIds(requiredOp RbacOp, subjectIds uuid[]) RETURNS SETOF RbacPermission STRICT LANGUAGE sql AS $$ @@ -435,78 +502,12 @@ CREATE OR REPLACE FUNCTION queryRequiredPermissionsOfSubjectIds(requiredOp RbacO ); $$; -abort; -set local session authorization restricted; -begin transaction; --- set local statement_timeout TO '5s'; -set local statement_timeout TO '5min'; -select count(*) from queryRequiredPermissionsOfSubjectIds('view', ARRAY[findRbacUserId('mike@hostsharing.net')]); -end transaction; +--// ---- - -abort; -set local session authorization default; -CREATE OR REPLACE FUNCTION queryAllPermissionsOfSubjectIds(subjectIds uuid[]) - RETURNS SETOF RbacPermission - STRICT - LANGUAGE sql AS $$ - SELECT DISTINCT * FROM RbacPermission WHERE uuid IN ( - WITH RECURSIVE grants AS ( - SELECT DISTINCT - descendantUuid, - ascendantUuid - FROM RbacGrants - WHERE - ascendantUuid = ANY(subjectIds) - UNION ALL - SELECT - "grant".descendantUuid, - "grant".ascendantUuid - FROM RbacGrants "grant" - INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid - ) SELECT - descendantUuid - FROM grants - ); - $$; - -abort; -set local session authorization restricted; -begin transaction; - set local statement_timeout TO '5s'; - select count(*) from queryAllPermissionsOfSubjectIds(ARRAY[findRbacUserId('mike@hostsharing.net')]); -end transaction; - ---- +--changeset rbac-base-query-users-with-permission-for-object:1 endDelimiter:--// /* -CREATE OR REPLACE FUNCTION queryAllPermissionsOfSubjectId(subjectId uuid) -- TODO: remove? - RETURNS SETOF RbacPermission - RETURNS NULL ON NULL INPUT - LANGUAGE sql AS $$ - SELECT * FROM RbacPermission WHERE uuid IN ( - WITH RECURSIVE grants AS ( - SELECT - descendantUuid, - ascendantUuid - FROM - RbacGrants - WHERE - ascendantUuid = subjectId - UNION ALL - SELECT - "grant".descendantUuid, - "grant".ascendantUuid - FROM RbacGrants "grant" - INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid - ) SELECT - descendantUuid - FROM - grants - ); -$$;*/ ---- + */ CREATE OR REPLACE FUNCTION queryAllRbacUsersWithPermissionsFor(objectId uuid) RETURNS SETOF RbacUser @@ -535,90 +536,12 @@ SELECT * FROM RbacUser WHERE uuid IN ( ); $$; +--// -CREATE OR REPLACE FUNCTION findGrantees(grantedId uuid) - RETURNS SETOF RbacReference - RETURNS NULL ON NULL INPUT - LANGUAGE sql AS $$ - SELECT reference.* - FROM ( - WITH RECURSIVE grants AS ( - SELECT - descendantUuid, - ascendantUuid - FROM - RbacGrants - WHERE - descendantUuid = grantedId - UNION ALL - SELECT - "grant".descendantUuid, - "grant".ascendantUuid - FROM - RbacGrants "grant" - INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid - ) SELECT - ascendantUuid - FROM - grants - ) as grantee - JOIN RbacReference reference ON reference.uuid=grantee.ascendantUuid; -$$; - -CREATE OR REPLACE FUNCTION isGranted(granteeId uuid, grantedId uuid) - RETURNS bool - RETURNS NULL ON NULL INPUT - LANGUAGE sql AS $$ - SELECT granteeId=grantedId OR granteeId IN ( - WITH RECURSIVE grants AS ( - SELECT descendantUuid, ascendantUuid - FROM RbacGrants - WHERE descendantUuid = grantedId - UNION ALL - SELECT "grant".descendantUuid, "grant".ascendantUuid - FROM RbacGrants "grant" - INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid - ) SELECT - ascendantUuid - FROM - grants - ); -$$; - -CREATE OR REPLACE FUNCTION isPermissionGrantedToSubject(permissionId uuid, subjectId uuid) - RETURNS BOOL - STABLE LEAKPROOF - LANGUAGE sql AS $$ - SELECT EXISTS ( - SELECT * FROM RbacUser WHERE uuid IN ( - WITH RECURSIVE grants AS ( - SELECT - descendantUuid, - ascendantUuid - FROM - RbacGrants g - WHERE - g.descendantUuid = permissionId - UNION ALL - SELECT - g.descendantUuid, - g.ascendantUuid - FROM - RbacGrants g - INNER JOIN grants recur ON recur.ascendantUuid = g.descendantUuid - ) SELECT - ascendantUuid - FROM - grants - WHERE ascendantUuid=subjectId - ) - ); -$$; - --- ======================================================== --- current user + assumed roles --- -------------------------------------------------------- +--changeset rbac-current-user:1 endDelimiter:--// +/* + */ CREATE OR REPLACE FUNCTION currentUser() RETURNS varchar(63) STABLE LEAKPROOF @@ -637,7 +560,6 @@ BEGIN RETURN currentUser; END; $$; -SET SESSION AUTHORIZATION DEFAULT; CREATE OR REPLACE FUNCTION currentUserId() RETURNS uuid STABLE LEAKPROOF @@ -652,6 +574,12 @@ BEGIN END; $$; +--// + +--changeset rbac-assumed-roles:1 endDelimiter:--// +/* + + */ CREATE OR REPLACE FUNCTION assumedRoles() RETURNS varchar(63)[] STABLE LEAKPROOF @@ -670,24 +598,27 @@ BEGIN RETURN string_to_array(currentSubject, ';'); END; $$; +CREATE OR REPLACE FUNCTION pureIdentifier(rawIdentifier varchar) + RETURNS uuid + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +begin + return regexp_replace(rawIdentifier, '\W+', ''); +end; $$; + CREATE OR REPLACE FUNCTION findUuidByIdName(objectTable varchar, objectIdName varchar) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE plpgsql AS $$ DECLARE - + sql varchar; BEGIN - /*sql = 'E ' || baseTable || '_historicize' || - ' AFTER INSERT OR DELETE OR UPDATE ON ' || baseTable || - ' FOR EACH ROW EXECUTE PROCEDURE historicize()'; - RAISE NOTICE 'sql: %', createTriggerSQL; - EXECUTE createTriggerSQ*/ - - RETURN customerUuidByIdName(objectIdName); + objectTable := pureIdentifier(objectTable); + objectIdName := pureIdentifier(objectIdName); + sql := objectTable || 'UuidByIdName(' || objectIdName || ');'; + EXECUTE sql; END; $$; -ROLLBACK; -SET SESSION AUTHORIZATION DEFAULT; CREATE OR REPLACE FUNCTION currentSubjectIds() RETURNS uuid[] STABLE LEAKPROOF @@ -731,3 +662,4 @@ BEGIN RETURN roleIdsToAssume; END; $$; +--// diff --git a/sql/21-hs-customer.sql b/src/main/resources/db/changelog/21-hs-customer.sql similarity index 100% rename from sql/21-hs-customer.sql rename to src/main/resources/db/changelog/21-hs-customer.sql diff --git a/sql/22-hs-packages.sql b/src/main/resources/db/changelog/22-hs-packages.sql similarity index 100% rename from sql/22-hs-packages.sql rename to src/main/resources/db/changelog/22-hs-packages.sql diff --git a/sql/23-hs-unixuser.sql b/src/main/resources/db/changelog/23-hs-unixuser.sql similarity index 100% rename from sql/23-hs-unixuser.sql rename to src/main/resources/db/changelog/23-hs-unixuser.sql diff --git a/sql/24-hs-domain.sql b/src/main/resources/db/changelog/24-hs-domain.sql similarity index 100% rename from sql/24-hs-domain.sql rename to src/main/resources/db/changelog/24-hs-domain.sql diff --git a/sql/25-hs-emailaddress.sql b/src/main/resources/db/changelog/25-hs-emailaddress.sql similarity index 100% rename from sql/25-hs-emailaddress.sql rename to src/main/resources/db/changelog/25-hs-emailaddress.sql diff --git a/sql/29-hs-statistics.sql b/src/main/resources/db/changelog/29-hs-statistics.sql similarity index 100% rename from sql/29-hs-statistics.sql rename to src/main/resources/db/changelog/29-hs-statistics.sql diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 00000000..0ba409d2 --- /dev/null +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,12 @@ +databaseChangeLog: + - include: + file: db/changelog/2022-07-28-001-last-row-count.sql + - include: + file: db/changelog/2022-07-28-002-int-to-var.sql + - include: + file: db/changelog/2022-07-28-003-random-in-range.sql + - include: + file: db/changelog/2022-07-28-004-uuid-ossp-extension.sql + - include: + file: db/changelog/2022-07-28-005-rbac-base.sql + From 583c45c85dd19387111b86b3483cbabe2528a8c9 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 28 Jul 2022 16:56:34 +0200 Subject: [PATCH 18/54] add first integration test based on Testcontainers --- .../hsadminng/RbacIntegrationTests.java | 26 +++++++++++++ src/test/resources/application.yml | 37 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java create mode 100644 src/test/resources/application.yml diff --git a/src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java b/src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java new file mode 100644 index 00000000..a6f39d89 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java @@ -0,0 +1,26 @@ +package net.hostsharing.hsadminng; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +@DataJpaTest +class RbacIntegrationTests { + + @PersistenceContext + private EntityManager em; + + @Test + @Transactional + void currentUser() { + em.createNativeQuery("SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';").executeUpdate(); + em.createNativeQuery("SET LOCAL hsadminng.assumedRoles = '';").executeUpdate(); + + final var result = em.createNativeQuery("select currentUser()").getSingleResult(); + Assertions.assertThat(result).isEqualTo("mike@hostsharing.net"); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml new file mode 100644 index 00000000..addd94d1 --- /dev/null +++ b/src/test/resources/application.yml @@ -0,0 +1,37 @@ +spring: + sql: + init: + platform: postgres + + datasource: + url: jdbc:tc:postgresql:12.9-alpine:///spring_boot_testcontainers + username: postgres + password: password + + jpa: + properties: + hibernate: + default_schema: public + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate: + ddl-auto: none + show-sql: true + + test: + database: + replace: none + + liquibase: + change-log: classpath:/db/changelog/db.changelog-master.yaml + contexts: test + +logging: + level: + liquibase: INFO + + # spring.datasource.driver-class-name=org.postgresql.Driver +# spring.datasource.url=${DB_URL} +# spring.datasource.username=${DB_USERNAME} +# spring.datasource.password=${DB_PASSWORD} + +# spring.jpa.properties.hibernate.default-schema=public From fb8862c37e286dcacd324e7fa022f28f8ac39164 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 28 Jul 2022 17:17:22 +0200 Subject: [PATCH 19/54] convert rbac-statistics and rbac-role-builder*.sql files to Liquibase changesets --- .../db/changelog/2022-07-28-005-rbac-base.sql | 9 --- ...l => 2022-07-28-020-rbac-role-builder.sql} | 58 ++++++++++++------- ...sql => 2022-07-28-030-rbac-statistics.sql} | 26 +++++---- .../db/changelog/db.changelog-master.yaml | 4 ++ 4 files changed, 58 insertions(+), 39 deletions(-) rename src/main/resources/db/changelog/{12-rbac-role-builder.sql => 2022-07-28-020-rbac-role-builder.sql} (81%) rename src/main/resources/db/changelog/{18-rbac-statistics.sql => 2022-07-28-030-rbac-statistics.sql} (58%) diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index e879852f..83e954ee 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -1,12 +1,3 @@ - --- ======================================================== --- RBAC --- -------------------------------------------------------- - -SET SESSION SESSION AUTHORIZATION DEFAULT; - --- https://arctype.com/blog/postgres-uuid/#creating-a-uuid-primary-key-using-uuid-osp-postgresql-example -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; --liquibase formatted sql --changeset rbac-base-reference:1 endDelimiter:--// diff --git a/src/main/resources/db/changelog/12-rbac-role-builder.sql b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql similarity index 81% rename from src/main/resources/db/changelog/12-rbac-role-builder.sql rename to src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql index 410a97e8..48127945 100644 --- a/src/main/resources/db/changelog/12-rbac-role-builder.sql +++ b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql @@ -1,12 +1,14 @@ +--liquibase formatted sql +-- ================================================================== +-- PERMISSIONS +--changeset rbac-role-builder-permissions:1 endDelimiter:--// +-- ------------------------------------------------------------------ --- ======================================================== --- Role-Hierarcy helper functions --- -------------------------------------------------------- +/* --- PERMISSIONS -------------------------------------------- + */ --- drop type RbacPermissions; CREATE TYPE RbacPermissions AS ( permissionUuids uuid[] @@ -19,15 +21,18 @@ BEGIN RETURN ROW(createPermissions(forObjectUuid, permitOps))::RbacPermissions; END; $$; --- SUPER ROLES -------------------------------------------- +--// --- drop type RbacSuperRoles; +--changeset rbac-role-builder-super-roles:1 endDelimiter:--// + +/* + + */ CREATE TYPE RbacSuperRoles AS ( roleUuids uuid[] ); --- drop function beneathRoles(roleDescriptors RbacRoleDescriptor[]) CREATE OR REPLACE FUNCTION beneathRoles(roleDescriptors RbacRoleDescriptor[]) RETURNS RbacSuperRoles LANGUAGE plpgsql STRICT AS $$ @@ -42,7 +47,6 @@ BEGIN RETURN ROW(superRoleUuids)::RbacSuperRoles; END; $$; --- drop function beneathRole(roleDescriptor RbacRoleDescriptor) CREATE OR REPLACE FUNCTION beneathRole(roleDescriptor RbacRoleDescriptor) RETURNS RbacSuperRoles LANGUAGE plpgsql STRICT AS $$ @@ -50,7 +54,6 @@ BEGIN RETURN beneathRoles(ARRAY[roleDescriptor]); END; $$; --- drop function beneathRole(roleUuid uuid); CREATE OR REPLACE FUNCTION beneathRole(roleUuid uuid) RETURNS RbacSuperRoles LANGUAGE plpgsql STRICT AS $$ @@ -58,7 +61,6 @@ BEGIN RETURN ROW(ARRAY[roleUuid]::uuid[])::RbacSuperRoles; END; $$; --- drop function asTopLevelRole(roleName varchar); CREATE OR REPLACE FUNCTION asTopLevelRole() RETURNS RbacSuperRoles LANGUAGE plpgsql STRICT AS $$ @@ -66,8 +68,16 @@ BEGIN RETURN ROW(ARRAY[]::uuid[])::RbacSuperRoles; END; $$; --- SUB ROLES ---------------------------------------------- +--// +-- ================================================================= +-- SUB ROLES +--changeset rbac-role-builder-sub-roles:1 endDelimiter:--// +-- ----------------------------------------------------------------- + +/* + + */ CREATE TYPE RbacSubRoles AS ( roleUuids uuid[] @@ -89,15 +99,20 @@ BEGIN RETURN beingItselfA(getRoleId(roleDescriptor, 'fail')); END; $$; --- USERS -------------------------------------------------- +--// --- drop type RbacUsers; +-- ================================================================= +-- USERS +--changeset rbac-role-builder-users:1 endDelimiter:--// +-- ----------------------------------------------------------------- + +/* +*/ CREATE TYPE RbacUsers AS ( userUuids uuid[] ); --- drop function withUsers(userNames varchar); CREATE OR REPLACE FUNCTION withUsers(userNames varchar[]) RETURNS RbacUsers LANGUAGE plpgsql STRICT AS $$ @@ -113,7 +128,6 @@ BEGIN END; $$; --- DROP FUNCTION withUser(userName varchar, whenNotExists RbacWhenNotExists); CREATE OR REPLACE FUNCTION withUser(userName varchar, whenNotExists RbacWhenNotExists = 'fail') RETURNS RbacUsers RETURNS NULL ON NULL INPUT @@ -122,11 +136,15 @@ BEGIN RETURN ROW(ARRAY[getRbacUserId(userName, whenNotExists )]); END; $$; --- ROLE NAME BUILDER -------------------------------------- +--// +-- ================================================================= +-- CREATE ROLE +--changeset rbac-role-builder-create-role:1 endDelimiter:--// +-- ----------------------------------------------------------------- --- CREATE ROLE MAIN FUNCTION ------------------------------ - +/* +*/ CREATE OR REPLACE FUNCTION createRole( roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, @@ -195,4 +213,4 @@ BEGIN RETURN createRole(roleDescriptor, permissions, null, subRoles, users); END; $$; - +--// diff --git a/src/main/resources/db/changelog/18-rbac-statistics.sql b/src/main/resources/db/changelog/2022-07-28-030-rbac-statistics.sql similarity index 58% rename from src/main/resources/db/changelog/18-rbac-statistics.sql rename to src/main/resources/db/changelog/2022-07-28-030-rbac-statistics.sql index 7bb5dac7..94d8a2d3 100644 --- a/src/main/resources/db/changelog/18-rbac-statistics.sql +++ b/src/main/resources/db/changelog/2022-07-28-030-rbac-statistics.sql @@ -1,18 +1,24 @@ +--liquibase formatted sql -DROP VIEW IF EXISTS "RbacStatisticsV"; -CREATE VIEW "RbacStatisticsV" AS - SELECT no, to_char("count", '9 999 999 999') as "count", "table" - FROM ( +--changeset rbac-statistics:1 endDelimiter:--// + +/* + Creates a view which presents some statistics about the RBAC tables. + */ +create view RbacStatisticsView AS + select no, to_char("count", '9 999 999 999') as "count", "table" + from ( select 1 as no, count(*) as "count", 'login users' as "table" from RbacUser - UNION + union select 2 as no, count(*) as "count", 'roles' as "table" from RbacRole - UNION + union select 3 as no, count(*) as "count", 'permissions' as "table" from RbacPermission - UNION + union select 4 as no, count(*) as "count", 'references' as "table" from RbacReference - UNION + union select 5 as no, count(*) as "count", 'grants' as "table" from RbacGrants - UNION + union select 6 as no, count(*) as "count", 'objects' as "table" from RbacObject ) as totals - ORDER BY totals.no; + order by totals.no; +--// diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 0ba409d2..ae24da75 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -9,4 +9,8 @@ databaseChangeLog: file: db/changelog/2022-07-28-004-uuid-ossp-extension.sql - include: file: db/changelog/2022-07-28-005-rbac-base.sql + - include: + file: db/changelog/2022-07-28-020-rbac-role-builder.sql + - include: + file: db/changelog/2022-07-28-030-rbac-statistics.sql From 4c403b04364a9adcca73c0bd74e2f03b9a950805 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 08:46:04 +0200 Subject: [PATCH 20/54] formatted SQL code --- .../resources/db/changelog/20-hs-base.sql | 35 +- .../changelog/2022-07-28-002-int-to-var.sql | 6 +- .../2022-07-28-003-random-in-range.sql | 4 +- .../2022-07-28-004-uuid-ossp-extension.sql | 2 +- .../db/changelog/2022-07-28-005-rbac-base.sql | 891 +++++++++--------- .../2022-07-28-020-rbac-role-builder.sql | 237 ++--- .../2022-07-28-030-rbac-statistics.sql | 34 +- .../changelog/2022-07-28-051-hs-customer.sql | 183 ++++ .../resources/db/changelog/21-hs-customer.sql | 169 ---- .../resources/db/changelog/22-hs-packages.sql | 180 ++-- .../resources/db/changelog/23-hs-unixuser.sql | 177 ++-- .../resources/db/changelog/24-hs-domain.sql | 167 ++-- .../db/changelog/25-hs-emailaddress.sql | 154 +-- .../db/changelog/29-hs-statistics.sql | 44 +- 14 files changed, 1167 insertions(+), 1116 deletions(-) create mode 100644 src/main/resources/db/changelog/2022-07-28-051-hs-customer.sql delete mode 100644 src/main/resources/db/changelog/21-hs-customer.sql diff --git a/src/main/resources/db/changelog/20-hs-base.sql b/src/main/resources/db/changelog/20-hs-base.sql index dcd8a252..b81aa4ae 100644 --- a/src/main/resources/db/changelog/20-hs-base.sql +++ b/src/main/resources/db/changelog/20-hs-base.sql @@ -1,22 +1,21 @@ - - - -CREATE TABLE Hostsharing +create table Hostsharing ( - uuid uuid PRIMARY KEY REFERENCES RbacObject(uuid) + uuid uuid primary key references RbacObject (uuid) ); -CREATE UNIQUE INDEX Hostsharing_Singleton ON Hostsharing ((0)); +create unique index Hostsharing_Singleton on Hostsharing ((0)); -INSERT INTO RbacObject (objecttable) VALUES ('hostsharing'); -INSERT INTO Hostsharing (uuid) VALUES ((SELECT uuid FROM RbacObject WHERE objectTable='hostsharing')); +insert +into RbacObject (objecttable) values ('hostsharing'); +insert + into Hostsharing (uuid) values ((select uuid from RbacObject where objectTable = 'hostsharing')); -CREATE OR REPLACE FUNCTION hostsharingAdmin() - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - STABLE LEAKPROOF - LANGUAGE sql AS $$ -SELECT 'global', (SELECT uuid FROM RbacObject WHERE objectTable='hostsharing'), 'admin'::RbacRoleType; +create or replace function hostsharingAdmin() + returns RbacRoleDescriptor + returns null on null input + stable leakproof + language sql as $$ +select 'global', (select uuid from RbacObject where objectTable = 'hostsharing'), 'admin'::RbacRoleType; $$; -- create administrators role with two assigned users @@ -32,7 +31,7 @@ do language plpgsql $$ $$; -BEGIN TRANSACTION; -SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; -select * from RbacUser where uuid=currentUserId(); -END TRANSACTION; +begin transaction; +set local hsadminng.currentUser = 'mike@hostsharing.net'; +select * from RbacUser where uuid = currentUserId(); +end transaction; diff --git a/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql b/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql index 7ef0efce..cccc037d 100644 --- a/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql +++ b/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql @@ -15,11 +15,11 @@ create or replace function intToVarChar(i integer, len integer) declare partial varchar; begin - select chr(ascii('a') + i%26) into partial; + select chr(ascii('a') + i % 26) into partial; if len > 1 then - return intToVarChar(i/26, len-1) || partial; + return intToVarChar(i / 26, len - 1) || partial; else return partial; end if; -END; $$; +end; $$; --// diff --git a/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql b/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql index 277984fc..41376770 100644 --- a/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql +++ b/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql @@ -12,9 +12,9 @@ create or replace function randomInRange(min integer, max integer) returns integer returns null on null input - language 'plpgsql' AS $$ + language 'plpgsql' as $$ begin - return floor(random() * (max-min + 1) + min); + return floor(random() * (max - min + 1) + min); end; $$; --// diff --git a/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql b/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql index 675b6ddb..45d6e799 100644 --- a/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql +++ b/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql @@ -5,5 +5,5 @@ /* Makes improved uuid generation available. */ -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +create extension if not exists "uuid-ossp"; --// diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index 83e954ee..1a91f0d0 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -4,26 +4,26 @@ /* */ -CREATE TYPE ReferenceType AS ENUM ('RbacUser', 'RbacRole', 'RbacPermission'); +create type ReferenceType as enum ('RbacUser', 'RbacRole', 'RbacPermission'); -CREATE TABLE RbacReference +create table RbacReference ( - uuid uuid UNIQUE DEFAULT uuid_generate_v4(), + uuid uuid unique default uuid_generate_v4(), type ReferenceType not null ); -CREATE OR REPLACE FUNCTION assertReferenceType(argument varchar, referenceId uuid, expectedType ReferenceType) - RETURNS ReferenceType - LANGUAGE plpgsql AS $$ -DECLARE +create or replace function assertReferenceType(argument varchar, referenceId uuid, expectedType ReferenceType) + returns ReferenceType + language plpgsql as $$ +declare actualType ReferenceType; -BEGIN - actualType = (SELECT type FROM RbacReference WHERE uuid=referenceId); - IF ( actualType <> expectedType ) THEN - RAISE EXCEPTION '% must reference a %, but got a %', argument, expectedType, actualType; +begin + actualType = (select type from RbacReference where uuid = referenceId); + if (actualType <> expectedType) then + raise exception '% must reference a %, but got a %', argument, expectedType, actualType; end if; - RETURN expectedType; -END; $$; + return expectedType; +end; $$; --// @@ -31,52 +31,54 @@ END; $$; /* */ -CREATE TABLE RbacUser +create table RbacUser ( - uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, + uuid uuid primary key references RbacReference (uuid) on delete cascade, name varchar(63) not null unique ); -CREATE OR REPLACE FUNCTION createRbacUser(userName varchar) - RETURNS uuid - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function createRbacUser(userName varchar) + returns uuid + returns null on null input + language plpgsql as $$ declare objectId uuid; -BEGIN - INSERT INTO RbacReference (type) VALUES ('RbacUser') RETURNING uuid INTO objectId; - INSERT INTO RbacUser (uuid, name) VALUES (objectid, userName); +begin + insert + into RbacReference (type) values ('RbacUser') returning uuid into objectId; + insert + into RbacUser (uuid, name) values (objectid, userName); return objectId; -END; +end; $$; -CREATE OR REPLACE FUNCTION findRbacUserId(userName varchar) - RETURNS uuid - RETURNS NULL ON NULL INPUT - LANGUAGE sql AS $$ -SELECT uuid FROM RbacUser WHERE name = userName +create or replace function findRbacUserId(userName varchar) + returns uuid + returns null on null input + language sql as $$ +select uuid from RbacUser where name = userName $$; -CREATE TYPE RbacWhenNotExists AS ENUM ('fail', 'create'); +create type RbacWhenNotExists as enum ('fail', 'create'); -CREATE OR REPLACE FUNCTION getRbacUserId(userName varchar, whenNotExists RbacWhenNotExists) - RETURNS uuid - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ -DECLARE +create or replace function getRbacUserId(userName varchar, whenNotExists RbacWhenNotExists) + returns uuid + returns null on null input + language plpgsql as $$ +declare userUuid uuid; -BEGIN +begin userUuid = findRbacUserId(userName); - IF ( userUuid IS NULL ) THEN - IF ( whenNotExists = 'fail') THEN - RAISE EXCEPTION 'RbacUser with name="%" not found', userName; - END IF; - IF ( whenNotExists = 'create') THEN + if (userUuid is null) then + if (whenNotExists = 'fail') then + raise exception 'RbacUser with name="%" not found', userName; + end if; + if (whenNotExists = 'create') then userUuid = createRbacUser(userName); - END IF; - END IF; + end if; + end if; return userUuid; -END; +end; $$; --// @@ -85,27 +87,29 @@ $$; /* */ -CREATE TABLE RbacObject +create table RbacObject ( - uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4(), + uuid uuid primary key default uuid_generate_v4(), objectTable varchar(64) not null, unique (objectTable, uuid) ); -CREATE OR REPLACE FUNCTION createRbacObject() - RETURNS trigger - LANGUAGE plpgsql STRICT AS $$ -DECLARE +create or replace function createRbacObject() + returns trigger + language plpgsql + strict as $$ +declare objectUuid uuid; -BEGIN - IF TG_OP = 'INSERT' THEN - INSERT INTO RbacObject (objectTable) VALUES (TG_TABLE_NAME) RETURNING uuid INTO objectUuid; +begin + if TG_OP = 'INSERT' then + insert + into RbacObject (objectTable) values (TG_TABLE_NAME) returning uuid into objectUuid; NEW.uuid = objectUuid; - RETURN NEW; - ELSE - RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; - END IF; -END; $$; + return NEW; + else + raise exception 'invalid usage of TRIGGER AFTER INSERT'; + end if; +end; $$; --// @@ -115,100 +119,102 @@ END; $$; */ -CREATE TYPE RbacRoleType AS ENUM ('owner', 'admin', 'tenant'); +create type RbacRoleType as enum ('owner', 'admin', 'tenant'); -CREATE TABLE RbacRole +create table RbacRole ( - uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, - objectUuid uuid references RbacObject(uuid) not null, - roleType RbacRoleType not null + uuid uuid primary key references RbacReference (uuid) on delete cascade, + objectUuid uuid references RbacObject (uuid) not null, + roleType RbacRoleType not null ); -CREATE TYPE RbacRoleDescriptor AS +create type RbacRoleDescriptor as ( - objectTable varchar(63), -- TODO: needed? remove? - objectUuid uuid, - roleType RbacRoleType + objectTable varchar(63), -- TODO: needed? remove? + objectUuid uuid, + roleType RbacRoleType ); -CREATE OR REPLACE FUNCTION roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType ) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT +create or replace function roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType) + returns RbacRoleDescriptor + returns null on null input -- STABLE LEAKPROOF - LANGUAGE sql AS $$ -SELECT objectTable, objectUuid, roleType::RbacRoleType; + language sql as $$ +select objectTable, objectUuid, roleType::RbacRoleType; $$; - -CREATE OR REPLACE FUNCTION createRole(roleDescriptor RbacRoleDescriptor) - RETURNS uuid - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function createRole(roleDescriptor RbacRoleDescriptor) + returns uuid + returns null on null input + language plpgsql as $$ declare referenceId uuid; -BEGIN - INSERT INTO RbacReference (type) VALUES ('RbacRole') RETURNING uuid INTO referenceId; - INSERT INTO RbacRole (uuid, objectUuid, roleType) VALUES (referenceId, roleDescriptor.objectUuid, roleDescriptor.roleType); +begin + insert + into RbacReference (type) values ('RbacRole') returning uuid into referenceId; + insert + into RbacRole (uuid, objectUuid, roleType) values (referenceId, roleDescriptor.objectUuid, roleDescriptor.roleType); return referenceId; -END; +end; $$; -CREATE OR REPLACE PROCEDURE deleteRole(roleUUid uuid) - LANGUAGE plpgsql AS $$ -BEGIN - DELETE FROM RbacRole WHERE uuid=roleUUid; -END; +create or replace procedure deleteRole(roleUUid uuid) + language plpgsql as $$ +begin + delete from RbacRole where uuid = roleUUid; +end; $$; -CREATE OR REPLACE FUNCTION findRoleId(roleDescriptor RbacRoleDescriptor) - RETURNS uuid - RETURNS NULL ON NULL INPUT - LANGUAGE sql AS $$ -SELECT uuid FROM RbacRole WHERE objectUuid = roleDescriptor.objectUuid AND roleType = roleDescriptor.roleType; +create or replace function findRoleId(roleDescriptor RbacRoleDescriptor) + returns uuid + returns null on null input + language sql as $$ +select uuid from RbacRole where objectUuid = roleDescriptor.objectUuid and roleType = roleDescriptor.roleType; $$; -CREATE OR REPLACE FUNCTION getRoleId(roleDescriptor RbacRoleDescriptor, whenNotExists RbacWhenNotExists) - RETURNS uuid - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ -DECLARE +create or replace function getRoleId(roleDescriptor RbacRoleDescriptor, whenNotExists RbacWhenNotExists) + returns uuid + returns null on null input + language plpgsql as $$ +declare roleUuid uuid; -BEGIN +begin roleUuid = findRoleId(roleDescriptor); - IF ( roleUuid IS NULL ) THEN - IF ( whenNotExists = 'fail') THEN - RAISE EXCEPTION 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType; - END IF; - IF ( whenNotExists = 'create') THEN + if (roleUuid is null) then + if (whenNotExists = 'fail') then + raise exception 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType; + end if; + if (whenNotExists = 'create') then roleUuid = createRole(roleDescriptor); - END IF; - END IF; + end if; + end if; return roleUuid; -END; +end; $$; --changeset rbac-base-permission:1 endDelimiter:--// /* */ -CREATE DOMAIN RbacOp AS VARCHAR(67) - CHECK( - VALUE = '*' - OR VALUE = 'delete' - OR VALUE = 'edit' - OR VALUE = 'view' - OR VALUE = 'assume' - OR VALUE ~ '^add-[a-z]+$' +create domain RbacOp as varchar(67) + check ( + VALUE = '*' + or VALUE = 'delete' + or VALUE = 'edit' + or VALUE = 'view' + or VALUE = 'assume' + or VALUE ~ '^add-[a-z]+$' ); -- DROP TABLE IF EXISTS RbacPermission; -CREATE TABLE RbacPermission -( uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, - objectUuid uuid not null references RbacObject, - op RbacOp not null, +create table RbacPermission +( + uuid uuid primary key references RbacReference (uuid) on delete cascade, + objectUuid uuid not null references RbacObject, + op RbacOp not null, unique (objectUuid, op) ); @@ -216,54 +222,60 @@ CREATE TABLE RbacPermission -- alter table rbacpermission add constraint rbacpermission_objectuuid_fkey foreign key (objectUuid) references rbacobject(uuid); -- alter table rbacpermission drop constraint rbacpermission_objectuuid; -CREATE OR REPLACE FUNCTION hasPermission(forObjectUuid uuid, forOp RbacOp) - RETURNS bool - LANGUAGE sql AS $$ - SELECT EXISTS ( - SELECT op - FROM RbacPermission p - WHERE p.objectUuid=forObjectUuid AND p.op in ('*', forOp) - ); - $$; - -CREATE OR REPLACE FUNCTION createPermissions(forObjectUuid uuid, permitOps RbacOp[]) - RETURNS uuid[] - LANGUAGE plpgsql AS $$ -DECLARE - refId uuid; - permissionIds uuid[] = ARRAY[]::uuid[]; -BEGIN - RAISE NOTICE 'createPermission for: % %', forObjectUuid, permitOps; - IF ( forObjectUuid IS NULL ) THEN - RAISE EXCEPTION 'forObjectUuid must not be null'; - END IF; - IF ( array_length(permitOps, 1) > 1 AND '*' = any(permitOps) ) THEN - RAISE EXCEPTION '"*" operation must not be assigned along with other operations: %', permitOps; - END IF; - - FOR i IN array_lower(permitOps, 1)..array_upper(permitOps, 1) LOOP - refId = (SELECT uuid FROM RbacPermission WHERE objectUuid=forObjectUuid AND op=permitOps[i]); - IF (refId IS NULL) THEN - RAISE NOTICE 'createPermission: % %', forObjectUuid, permitOps[i]; - INSERT INTO RbacReference ("type") VALUES ('RbacPermission') RETURNING uuid INTO refId; - INSERT INTO RbacPermission (uuid, objectUuid, op) VALUES (refId, forObjectUuid, permitOps[i]); - END IF; - RAISE NOTICE 'addPermission: %', refId; - permissionIds = permissionIds || refId; - END LOOP; - - RAISE NOTICE 'createPermissions returning: %', permissionIds; - return permissionIds; -END; +create or replace function hasPermission(forObjectUuid uuid, forOp RbacOp) + returns bool + language sql as $$ +select exists( + select op + from RbacPermission p + where p.objectUuid = forObjectUuid + and p.op in ('*', forOp) + ); $$; -CREATE OR REPLACE FUNCTION findPermissionId(forObjectUuid uuid, forOp RbacOp) - RETURNS uuid - RETURNS NULL ON NULL INPUT - STABLE LEAKPROOF - LANGUAGE sql AS $$ - SELECT uuid FROM RbacPermission p - WHERE p.objectUuid=forObjectUuid AND p.op in ('*', forOp) +create or replace function createPermissions(forObjectUuid uuid, permitOps RbacOp[]) + returns uuid[] + language plpgsql as $$ +declare + refId uuid; + permissionIds uuid[] = array []::uuid[]; +begin + raise notice 'createPermission for: % %', forObjectUuid, permitOps; + if (forObjectUuid is null) then + raise exception 'forObjectUuid must not be null'; + end if; + if (array_length(permitOps, 1) > 1 and '*' = any (permitOps)) then + raise exception '"*" operation must not be assigned along with other operations: %', permitOps; + end if; + + for i in array_lower(permitOps, 1)..array_upper(permitOps, 1) + loop + refId = (select uuid from RbacPermission where objectUuid = forObjectUuid and op = permitOps[i]); + if (refId is null) then + raise notice 'createPermission: % %', forObjectUuid, permitOps[i]; + insert + into RbacReference ("type") values ('RbacPermission') returning uuid into refId; + insert + into RbacPermission (uuid, objectUuid, op) values (refId, forObjectUuid, permitOps[i]); + end if; + raise notice 'addPermission: %', refId; + permissionIds = permissionIds || refId; + end loop; + + raise notice 'createPermissions returning: %', permissionIds; + return permissionIds; +end; +$$; + +create or replace function findPermissionId(forObjectUuid uuid, forOp RbacOp) + returns uuid + returns null on null input + stable leakproof + language sql as $$ +select uuid + from RbacPermission p + where p.objectUuid = forObjectUuid + and p.op in ('*', forOp) $$; --// @@ -272,192 +284,174 @@ $$; /* */ -CREATE TABLE RbacGrants +create table RbacGrants ( - ascendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, - descendantUuid uuid references RbacReference (uuid) ON DELETE CASCADE, - follow boolean not null default true, + ascendantUuid uuid references RbacReference (uuid) on delete cascade, + descendantUuid uuid references RbacReference (uuid) on delete cascade, + follow boolean not null default true, primary key (ascendantUuid, descendantUuid) ); -CREATE INDEX ON RbacGrants (ascendantUuid); -CREATE INDEX ON RbacGrants (descendantUuid); +create index on RbacGrants (ascendantUuid); +create index on RbacGrants (descendantUuid); --// -CREATE OR REPLACE FUNCTION findGrantees(grantedId uuid) - RETURNS SETOF RbacReference - RETURNS NULL ON NULL INPUT - LANGUAGE sql AS $$ -SELECT reference.* -FROM ( - WITH RECURSIVE grants AS ( - SELECT - descendantUuid, - ascendantUuid - FROM - RbacGrants - WHERE - descendantUuid = grantedId - UNION ALL - SELECT - "grant".descendantUuid, - "grant".ascendantUuid - FROM - RbacGrants "grant" - INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid - ) SELECT - ascendantUuid - FROM - grants - ) as grantee - JOIN RbacReference reference ON reference.uuid=grantee.ascendantUuid; +create or replace function findGrantees(grantedId uuid) + returns setof RbacReference + returns null on null input + language sql as $$ +select reference.* + from (with recursive grants as (select descendantUuid, + ascendantUuid + from RbacGrants + where descendantUuid = grantedId + union all + select "grant".descendantUuid, + "grant".ascendantUuid + from RbacGrants "grant" + inner join grants recur on recur.ascendantUuid = "grant".descendantUuid) + select ascendantUuid + from grants) as grantee + join RbacReference reference on reference.uuid = grantee.ascendantUuid; $$; -CREATE OR REPLACE FUNCTION isGranted(granteeId uuid, grantedId uuid) - RETURNS bool - RETURNS NULL ON NULL INPUT - LANGUAGE sql AS $$ -SELECT granteeId=grantedId OR granteeId IN ( - WITH RECURSIVE grants AS ( - SELECT descendantUuid, ascendantUuid - FROM RbacGrants - WHERE descendantUuid = grantedId - UNION ALL - SELECT "grant".descendantUuid, "grant".ascendantUuid - FROM RbacGrants "grant" - INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid - ) SELECT - ascendantUuid - FROM - grants -); +create or replace function isGranted(granteeId uuid, grantedId uuid) + returns bool + returns null on null input + language sql as $$ +select granteeId = grantedId or granteeId in (with recursive grants as (select descendantUuid, ascendantUuid + from RbacGrants + where descendantUuid = grantedId + union all + select "grant".descendantUuid, "grant".ascendantUuid + from RbacGrants "grant" + inner join grants recur on recur.ascendantUuid = "grant".descendantUuid) + select ascendantUuid + from grants); $$; -CREATE OR REPLACE FUNCTION isPermissionGrantedToSubject(permissionId uuid, subjectId uuid) - RETURNS BOOL - STABLE LEAKPROOF - LANGUAGE sql AS $$ -SELECT EXISTS ( - SELECT * FROM RbacUser WHERE uuid IN ( - WITH RECURSIVE grants AS ( - SELECT - descendantUuid, - ascendantUuid - FROM - RbacGrants g - WHERE - g.descendantUuid = permissionId - UNION ALL - SELECT - g.descendantUuid, - g.ascendantUuid - FROM - RbacGrants g - INNER JOIN grants recur ON recur.ascendantUuid = g.descendantUuid - ) SELECT - ascendantUuid - FROM - grants - WHERE ascendantUuid=subjectId - ) +create or replace function isPermissionGrantedToSubject(permissionId uuid, subjectId uuid) + returns BOOL + stable leakproof + language sql as $$ +select exists( + select * + from RbacUser + where uuid in (with recursive grants as (select descendantUuid, + ascendantUuid + from RbacGrants g + where g.descendantUuid = permissionId + union all + select g.descendantUuid, + g.ascendantUuid + from RbacGrants g + inner join grants recur on recur.ascendantUuid = g.descendantUuid) + select ascendantUuid + from grants + where ascendantUuid = subjectId) ); $$; -CREATE OR REPLACE PROCEDURE grantPermissionsToRole(roleUuid uuid, permissionIds uuid[]) - LANGUAGE plpgsql AS $$ -BEGIN - RAISE NOTICE 'grantPermissionsToRole: % -> %', roleUuid, permissionIds; - FOR i IN array_lower(permissionIds, 1)..array_upper(permissionIds, 1) LOOP - perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole'); - perform assertReferenceType('permissionId (descendant)', permissionIds[i], 'RbacPermission'); +create or replace procedure grantPermissionsToRole(roleUuid uuid, permissionIds uuid[]) + language plpgsql as $$ +begin + raise notice 'grantPermissionsToRole: % -> %', roleUuid, permissionIds; + for i in array_lower(permissionIds, 1)..array_upper(permissionIds, 1) + loop + perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole'); + perform assertReferenceType('permissionId (descendant)', permissionIds[i], 'RbacPermission'); - -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (roleId, permissionIds[i], true); -- assumeV1 - INSERT INTO RbacGrants (ascendantUuid, descendantUuid) VALUES (roleUuid, permissionIds[i]); - END LOOP; -END; + -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (roleId, permissionIds[i], true); -- assumeV1 + insert + into RbacGrants (ascendantUuid, descendantUuid) values (roleUuid, permissionIds[i]); + end loop; +end; $$; -CREATE OR REPLACE PROCEDURE grantRoleToRole(subRoleId uuid, superRoleId uuid, doFollow bool = true ) - LANGUAGE plpgsql AS $$ -BEGIN +create or replace procedure grantRoleToRole(subRoleId uuid, superRoleId uuid, doFollow bool = true) + language plpgsql as $$ +begin perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole'); - perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole'); + perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole'); - IF ( isGranted(subRoleId, superRoleId) ) THEN - RAISE EXCEPTION 'Cyclic role grant detected between % and %', subRoleId, superRoleId; - END IF; + if (isGranted(subRoleId, superRoleId)) then + raise exception 'Cyclic role grant detected between % and %', subRoleId, superRoleId; + end if; -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (superRoleId, subRoleId, doapply); -- assumeV1 - INSERT INTO RbacGrants (ascendantUuid, descendantUuid, follow) VALUES (superRoleId, subRoleId, doFollow) - ON CONFLICT DO NOTHING ; -- TODO: remove? -END; $$; + insert + into RbacGrants (ascendantUuid, descendantUuid, follow) + values (superRoleId, subRoleId, doFollow) + on conflict do nothing; -- TODO: remove? +end; $$; -CREATE OR REPLACE PROCEDURE revokeRoleFromRole(subRoleId uuid, superRoleId uuid) - LANGUAGE plpgsql AS $$ -BEGIN +create or replace procedure revokeRoleFromRole(subRoleId uuid, superRoleId uuid) + language plpgsql as $$ +begin perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole'); - perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole'); + perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole'); - IF ( isGranted(subRoleId, superRoleId) ) THEN - DELETE FROM RbacGrants WHERE ascendantUuid=superRoleId AND descendantUuid=subRoleId; - END IF; -END; $$; + if (isGranted(subRoleId, superRoleId)) then + delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId; + end if; +end; $$; -CREATE OR REPLACE PROCEDURE grantRoleToUser(roleId uuid, userId uuid) - LANGUAGE plpgsql AS $$ -BEGIN +create or replace procedure grantRoleToUser(roleId uuid, userId uuid) + language plpgsql as $$ +begin perform assertReferenceType('roleId (ascendant)', roleId, 'RbacRole'); - perform assertReferenceType('userId (descendant)', userId, 'RbacUser'); + perform assertReferenceType('userId (descendant)', userId, 'RbacUser'); -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (userId, roleId, true); -- assumeV1 - INSERT INTO RbacGrants (ascendantUuid, descendantUuid) VALUES (userId, roleId) - ON CONFLICT DO NOTHING ; -- TODO: remove? -END; $$; + insert + into RbacGrants (ascendantUuid, descendantUuid) + values (userId, roleId) + on conflict do nothing; -- TODO: remove? +end; $$; --// --changeset rbac-base-query-accessible-object-uuids:1 endDelimiter:--// /* */ -CREATE OR REPLACE FUNCTION queryAccessibleObjectUuidsOfSubjectIds( - requiredOp RbacOp, - forObjectTable varchar, -- reduces the result set, but is not really faster when used in restricted view - subjectIds uuid[], - maxObjects integer = 8000) - RETURNS SETOF uuid - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ - DECLARE - foundRows bigint; - BEGIN - RETURN QUERY SELECT DISTINCT perm.objectUuid - FROM ( - WITH RECURSIVE grants AS ( - SELECT descendantUuid, ascendantUuid, 1 AS level - FROM RbacGrants - WHERE follow AND ascendantUuid = ANY(subjectIds) - UNION DISTINCT - SELECT "grant".descendantUuid, "grant".ascendantUuid, level+1 AS level - FROM RbacGrants "grant" - INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid - WHERE follow - ) SELECT descendantUuid - FROM grants - ) as granted - JOIN RbacPermission perm - ON granted.descendantUuid=perm.uuid AND perm.op IN ('*', requiredOp) - JOIN RbacObject obj ON obj.uuid=perm.objectUuid AND obj.objectTable=forObjectTable - LIMIT maxObjects+1; +create or replace function queryAccessibleObjectUuidsOfSubjectIds( + requiredOp RbacOp, + forObjectTable varchar, -- reduces the result set, but is not really faster when used in restricted view + subjectIds uuid[], + maxObjects integer = 8000) + returns setof uuid + returns null on null input + language plpgsql as $$ +declare + foundRows bigint; +begin + return query select distinct perm.objectUuid + from (with recursive grants as (select descendantUuid, ascendantUuid, 1 as level + from RbacGrants + where follow + and ascendantUuid = any (subjectIds) + union + distinct + select "grant".descendantUuid, "grant".ascendantUuid, level + 1 as level + from RbacGrants "grant" + inner join grants recur on recur.descendantUuid = "grant".ascendantUuid + where follow) + select descendantUuid + from grants) as granted + join RbacPermission perm + on granted.descendantUuid = perm.uuid and perm.op in ('*', requiredOp) + join RbacObject obj on obj.uuid = perm.objectUuid and obj.objectTable = forObjectTable + limit maxObjects + 1; - foundRows = lastRowCount(); - IF foundRows > maxObjects THEN - RAISE EXCEPTION 'Too many accessible objects, limit is %, found %.', maxObjects, foundRows - USING - ERRCODE = 'P0003', - HINT = 'Please assume a sub-role and try again.'; - END IF; - END; + foundRows = lastRowCount(); + if foundRows > maxObjects then + raise exception 'Too many accessible objects, limit is %, found %.', maxObjects, foundRows + using + errcode = 'P0003', + hint = 'Please assume a sub-role and try again.'; + end if; +end; $$; --// @@ -466,31 +460,25 @@ $$; /* */ -CREATE OR REPLACE FUNCTION queryGrantedPermissionsOfSubjectIds(requiredOp RbacOp, subjectIds uuid[]) - RETURNS SETOF RbacPermission - STRICT - LANGUAGE sql AS $$ - SELECT DISTINCT * - FROM RbacPermission - WHERE op = '*' OR op = requiredOp - AND uuid IN ( - WITH RECURSIVE grants AS ( - SELECT DISTINCT - descendantUuid, - ascendantUuid - FROM RbacGrants - WHERE - ascendantUuid = ANY(subjectIds) - UNION ALL - SELECT - "grant".descendantUuid, - "grant".ascendantUuid - FROM RbacGrants "grant" - INNER JOIN grants recur ON recur.descendantUuid = "grant".ascendantUuid - ) SELECT - descendantUuid - FROM grants - ); +create or replace function queryGrantedPermissionsOfSubjectIds(requiredOp RbacOp, subjectIds uuid[]) + returns setof RbacPermission + strict + language sql as $$ +select distinct * + from RbacPermission + where op = '*' + or op = requiredOp + and uuid in (with recursive grants as (select distinct descendantUuid, + ascendantUuid + from RbacGrants + where ascendantUuid = any (subjectIds) + union all + select "grant".descendantUuid, + "grant".ascendantUuid + from RbacGrants "grant" + inner join grants recur on recur.descendantUuid = "grant".ascendantUuid) + select descendantUuid + from grants); $$; --// @@ -500,31 +488,23 @@ $$; */ -CREATE OR REPLACE FUNCTION queryAllRbacUsersWithPermissionsFor(objectId uuid) - RETURNS SETOF RbacUser - RETURNS NULL ON NULL INPUT - LANGUAGE sql AS $$ -SELECT * FROM RbacUser WHERE uuid IN ( - WITH RECURSIVE grants AS ( - SELECT - descendantUuid, - ascendantUuid - FROM - RbacGrants - WHERE - descendantUuid = objectId - UNION ALL - SELECT - "grant".descendantUuid, - "grant".ascendantUuid - FROM - RbacGrants "grant" - INNER JOIN grants recur ON recur.ascendantUuid = "grant".descendantUuid - ) SELECT - ascendantUuid - FROM - grants -); +create or replace function queryAllRbacUsersWithPermissionsFor(objectId uuid) + returns setof RbacUser + returns null on null input + language sql as $$ +select * + from RbacUser + where uuid in (with recursive grants as (select descendantUuid, + ascendantUuid + from RbacGrants + where descendantUuid = objectId + union all + select "grant".descendantUuid, + "grant".ascendantUuid + from RbacGrants "grant" + inner join grants recur on recur.ascendantUuid = "grant".descendantUuid) + select ascendantUuid + from grants); $$; --// @@ -533,36 +513,37 @@ $$; /* */ -CREATE OR REPLACE FUNCTION currentUser() - RETURNS varchar(63) - STABLE LEAKPROOF - LANGUAGE plpgsql AS $$ -DECLARE - currentUser VARCHAR(63); -BEGIN - BEGIN +create or replace function currentUser() + returns varchar(63) + stable leakproof + language plpgsql as $$ +declare + currentUser varchar(63); +begin + begin currentUser := current_setting('hsadminng.currentUser'); - EXCEPTION WHEN OTHERS THEN - currentUser := NULL; - END; - IF (currentUser IS NULL OR currentUser = '') THEN - RAISE EXCEPTION 'hsadminng.currentUser must be defined, please use "SET LOCAL ...;"'; - END IF; - RETURN currentUser; -END; $$; + exception + when others then + currentUser := null; + end; + if (currentUser is null or currentUser = '') then + raise exception 'hsadminng.currentUser must be defined, please use "SET LOCAL ...;"'; + end if; + return currentUser; +end; $$; -CREATE OR REPLACE FUNCTION currentUserId() - RETURNS uuid - STABLE LEAKPROOF - LANGUAGE plpgsql AS $$ -DECLARE - currentUser VARCHAR(63); +create or replace function currentUserId() + returns uuid + stable leakproof + language plpgsql as $$ +declare + currentUser varchar(63); currentUserId uuid; -BEGIN +begin currentUser := currentUser(); - currentUserId = (SELECT uuid FROM RbacUser WHERE name = currentUser); - RETURN currentUserId; -END; $$; + currentUserId = (select uuid from RbacUser where name = currentUser); + return currentUserId; +end; $$; --// @@ -571,86 +552,90 @@ END; $$; /* */ -CREATE OR REPLACE FUNCTION assumedRoles() - RETURNS varchar(63)[] - STABLE LEAKPROOF - LANGUAGE plpgsql AS $$ -DECLARE - currentSubject VARCHAR(63); -BEGIN - BEGIN +create or replace function assumedRoles() + returns varchar(63)[] + stable leakproof + language plpgsql as $$ +declare + currentSubject varchar(63); +begin + begin currentSubject := current_setting('hsadminng.assumedRoles'); - EXCEPTION WHEN OTHERS THEN - RETURN ARRAY[]::varchar[]; - END; - IF (currentSubject = '') THEN - RETURN ARRAY[]::varchar[]; - END IF; - RETURN string_to_array(currentSubject, ';'); - END; $$; + exception + when others then + return array []::varchar[]; + end; + if (currentSubject = '') then + return array []::varchar[]; + end if; + return string_to_array(currentSubject, ';'); +end; $$; -CREATE OR REPLACE FUNCTION pureIdentifier(rawIdentifier varchar) - RETURNS uuid - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function pureIdentifier(rawIdentifier varchar) + returns uuid + returns null on null input + language plpgsql as $$ begin return regexp_replace(rawIdentifier, '\W+', ''); end; $$; -CREATE OR REPLACE FUNCTION findUuidByIdName(objectTable varchar, objectIdName varchar) - RETURNS uuid - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ -DECLARE +create or replace function findUuidByIdName(objectTable varchar, objectIdName varchar) + returns uuid + returns null on null input + language plpgsql as $$ +declare sql varchar; -BEGIN +begin objectTable := pureIdentifier(objectTable); objectIdName := pureIdentifier(objectIdName); sql := objectTable || 'UuidByIdName(' || objectIdName || ');'; - EXECUTE sql; -END; $$; + execute sql; +end; $$; -CREATE OR REPLACE FUNCTION currentSubjectIds() - RETURNS uuid[] - STABLE LEAKPROOF - LANGUAGE plpgsql AS $$ -DECLARE - currentUserId uuid; - roleNames VARCHAR(63)[]; - roleName VARCHAR(63); - objectTableToAssume VARCHAR(63); - objectNameToAssume VARCHAR(63); - objectUuidToAssume uuid; - roleTypeToAssume RbacRoleType; - roleIdsToAssume uuid[]; - roleUuidToAssume uuid; -BEGIN +create or replace function currentSubjectIds() + returns uuid[] + stable leakproof + language plpgsql as $$ +declare + currentUserId uuid; + roleNames varchar(63)[]; + roleName varchar(63); + objectTableToAssume varchar(63); + objectNameToAssume varchar(63); + objectUuidToAssume uuid; + roleTypeToAssume RbacRoleType; + roleIdsToAssume uuid[]; + roleUuidToAssume uuid; +begin currentUserId := currentUserId(); roleNames := assumedRoles(); - IF ( CARDINALITY(roleNames) = 0 ) THEN - RETURN ARRAY[currentUserId]; - END IF; + if (cardinality(roleNames) = 0) then + return array [currentUserId]; + end if; - RAISE NOTICE 'assuming roles: %', roleNames; + raise notice 'assuming roles: %', roleNames; - FOREACH roleName IN ARRAY roleNames LOOP - roleName = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), '.')); - objectTableToAssume = split_part(roleName, '#', 1); - objectNameToAssume = split_part(roleName, '#', 2); - roleTypeToAssume = split_part(roleName, '#', 3); + foreach roleName in array roleNames + loop + roleName = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), '.')); + objectTableToAssume = split_part(roleName, '#', 1); + objectNameToAssume = split_part(roleName, '#', 2); + roleTypeToAssume = split_part(roleName, '#', 3); - objectUuidToAssume = findUuidByIdName(objectTableToAssume, objectNameToAssume); + objectUuidToAssume = findUuidByIdName(objectTableToAssume, objectNameToAssume); - -- TODO: either the result needs to be cached at least per transaction or we need to get rid of SELCT in a loop - SELECT uuid AS roleuuidToAssume - FROM RbacRole r - WHERE r.objectUuid=objectUuidToAssume AND r.roleType=roleTypeToAssume INTO roleUuidToAssume; - IF ( NOT isGranted(currentUserId, roleUuidToAssume) ) THEN - RAISE EXCEPTION 'user % has no permission to assume role %', currentUser(), roleUuidToAssume; - END IF; - roleIdsToAssume := roleIdsToAssume || roleUuidToAssume; - END LOOP; + -- TODO: either the result needs to be cached at least per transaction or we need to get rid of SELCT in a loop + select uuid as roleuuidToAssume + from RbacRole r + where r.objectUuid = objectUuidToAssume + and r.roleType = roleTypeToAssume + into roleUuidToAssume; + if (not isGranted(currentUserId, roleUuidToAssume)) then + raise exception 'user % has no permission to assume role %', currentUser(), roleUuidToAssume; + end if; + roleIdsToAssume := roleIdsToAssume || roleUuidToAssume; + end loop; - RETURN roleIdsToAssume; -END; $$; + return roleIdsToAssume; +end; $$; --// diff --git a/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql index 48127945..9079100b 100644 --- a/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql +++ b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql @@ -9,17 +9,18 @@ */ -CREATE TYPE RbacPermissions AS +create type RbacPermissions as ( permissionUuids uuid[] ); -CREATE OR REPLACE FUNCTION grantingPermissions(forObjectUuid uuid, permitOps RbacOp[]) - RETURNS RbacPermissions - LANGUAGE plpgsql STRICT AS $$ -BEGIN - RETURN ROW(createPermissions(forObjectUuid, permitOps))::RbacPermissions; -END; $$; +create or replace function grantingPermissions(forObjectUuid uuid, permitOps RbacOp[]) + returns RbacPermissions + language plpgsql + strict as $$ +begin + return row (createPermissions(forObjectUuid, permitOps))::RbacPermissions; +end; $$; --// @@ -28,45 +29,50 @@ END; $$; /* */ -CREATE TYPE RbacSuperRoles AS +create type RbacSuperRoles as ( roleUuids uuid[] ); -CREATE OR REPLACE FUNCTION beneathRoles(roleDescriptors RbacRoleDescriptor[]) - RETURNS RbacSuperRoles - LANGUAGE plpgsql STRICT AS $$ -DECLARE +create or replace function beneathRoles(roleDescriptors RbacRoleDescriptor[]) + returns RbacSuperRoles + language plpgsql + strict as $$ +declare superRoleDescriptor RbacRoleDescriptor; - superRoleUuids uuid[] := ARRAY[]::uuid[]; -BEGIN - FOREACH superRoleDescriptor IN ARRAY roleDescriptors LOOP - superRoleUuids := superRoleUuids || getRoleId(superRoleDescriptor, 'fail'); - END LOOP; + superRoleUuids uuid[] := array []::uuid[]; +begin + foreach superRoleDescriptor in array roleDescriptors + loop + superRoleUuids := superRoleUuids || getRoleId(superRoleDescriptor, 'fail'); + end loop; - RETURN ROW(superRoleUuids)::RbacSuperRoles; -END; $$; + return row (superRoleUuids)::RbacSuperRoles; +end; $$; -CREATE OR REPLACE FUNCTION beneathRole(roleDescriptor RbacRoleDescriptor) - RETURNS RbacSuperRoles - LANGUAGE plpgsql STRICT AS $$ -BEGIN - RETURN beneathRoles(ARRAY[roleDescriptor]); -END; $$; +create or replace function beneathRole(roleDescriptor RbacRoleDescriptor) + returns RbacSuperRoles + language plpgsql + strict as $$ +begin + return beneathRoles(array [roleDescriptor]); +end; $$; -CREATE OR REPLACE FUNCTION beneathRole(roleUuid uuid) - RETURNS RbacSuperRoles - LANGUAGE plpgsql STRICT AS $$ -BEGIN - RETURN ROW(ARRAY[roleUuid]::uuid[])::RbacSuperRoles; -END; $$; +create or replace function beneathRole(roleUuid uuid) + returns RbacSuperRoles + language plpgsql + strict as $$ +begin + return row (array [roleUuid]::uuid[])::RbacSuperRoles; +end; $$; -CREATE OR REPLACE FUNCTION asTopLevelRole() - RETURNS RbacSuperRoles - LANGUAGE plpgsql STRICT AS $$ -BEGIN - RETURN ROW(ARRAY[]::uuid[])::RbacSuperRoles; -END; $$; +create or replace function asTopLevelRole() + returns RbacSuperRoles + language plpgsql + strict as $$ +begin + return row (array []::uuid[])::RbacSuperRoles; +end; $$; --// @@ -78,26 +84,28 @@ END; $$; /* */ -CREATE TYPE RbacSubRoles AS +create type RbacSubRoles as ( roleUuids uuid[] ); -- drop FUNCTION beingItselfA(roleUuid uuid) -CREATE OR REPLACE FUNCTION beingItselfA(roleUuid uuid) - RETURNS RbacSubRoles - LANGUAGE plpgsql STRICT AS $$ -BEGIN - RETURN ROW(ARRAY[roleUuid]::uuid[])::RbacSubRoles; -END; $$; +create or replace function beingItselfA(roleUuid uuid) + returns RbacSubRoles + language plpgsql + strict as $$ +begin + return row (array [roleUuid]::uuid[])::RbacSubRoles; +end; $$; -- drop FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor) -CREATE OR REPLACE FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor) - RETURNS RbacSubRoles - LANGUAGE plpgsql STRICT AS $$ -BEGIN - RETURN beingItselfA(getRoleId(roleDescriptor, 'fail')); -END; $$; +create or replace function beingItselfA(roleDescriptor RbacRoleDescriptor) + returns RbacSubRoles + language plpgsql + strict as $$ +begin + return beingItselfA(getRoleId(roleDescriptor, 'fail')); +end; $$; --// @@ -108,33 +116,35 @@ END; $$; /* */ -CREATE TYPE RbacUsers AS +create type RbacUsers as ( userUuids uuid[] ); -CREATE OR REPLACE FUNCTION withUsers(userNames varchar[]) - RETURNS RbacUsers - LANGUAGE plpgsql STRICT AS $$ -DECLARE - userName varchar; - userUuids uuid[] := ARRAY[]::uuid[]; -BEGIN - FOREACH userName IN ARRAY userNames LOOP - userUuids := userUuids || getRbacUserId(userName, 'fail'); - END LOOP; +create or replace function withUsers(userNames varchar[]) + returns RbacUsers + language plpgsql + strict as $$ +declare + userName varchar; + userUuids uuid[] := array []::uuid[]; +begin + foreach userName in array userNames + loop + userUuids := userUuids || getRbacUserId(userName, 'fail'); + end loop; - RETURN ROW(userUuids)::RbacUsers; -END; $$; + return row (userUuids)::RbacUsers; +end; $$; -CREATE OR REPLACE FUNCTION withUser(userName varchar, whenNotExists RbacWhenNotExists = 'fail') - RETURNS RbacUsers - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ -BEGIN - RETURN ROW(ARRAY[getRbacUserId(userName, whenNotExists )]); -END; $$; +create or replace function withUser(userName varchar, whenNotExists RbacWhenNotExists = 'fail') + returns RbacUsers + returns null on null input + language plpgsql as $$ +begin + return row (array [getRbacUserId(userName, whenNotExists)]); +end; $$; --// @@ -145,72 +155,75 @@ END; $$; /* */ -CREATE OR REPLACE FUNCTION createRole( +create or replace function createRole( roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, superRoles RbacSuperRoles, subRoles RbacSubRoles = null, users RbacUsers = null ) - RETURNS uuid - CALLED ON NULL INPUT - LANGUAGE plpgsql AS $$ -DECLARE - roleUuid uuid; + returns uuid + called on null input + language plpgsql as $$ +declare + roleUuid uuid; superRoleUuid uuid; - subRoleUuid uuid; - userUuid uuid; -BEGIN - RAISE NOTICE 'will createRole for %', roleDescriptor; - RAISE NOTICE 'will createRole for % % %', roleDescriptor.objecttable, roleDescriptor.objectuuid, roleDescriptor.roletype; + subRoleUuid uuid; + userUuid uuid; +begin + raise notice 'will createRole for %', roleDescriptor; + raise notice 'will createRole for % % %', roleDescriptor.objecttable, roleDescriptor.objectuuid, roleDescriptor.roletype; roleUuid = createRole(roleDescriptor); call grantPermissionsToRole(roleUuid, permissions.permissionUuids); - IF superRoles IS NOT NULL THEN - FOREACH superRoleUuid IN ARRAY superRoles.roleuUids LOOP - call grantRoleToRole(roleUuid, superRoleUuid); - END LOOP; - END IF; + if superRoles is not null then + foreach superRoleUuid in array superRoles.roleuUids + loop + call grantRoleToRole(roleUuid, superRoleUuid); + end loop; + end if; - IF subRoles IS NOT NULL THEN - FOREACH subRoleUuid IN ARRAY subRoles.roleuUids LOOP - call grantRoleToRole(subRoleUuid, roleUuid); - END LOOP; - END IF; + if subRoles is not null then + foreach subRoleUuid in array subRoles.roleuUids + loop + call grantRoleToRole(subRoleUuid, roleUuid); + end loop; + end if; - IF users IS NOT NULL THEN - FOREACH userUuid IN ARRAY users.useruUids LOOP - call grantRoleToUser(roleUuid, userUuid); - END LOOP; - END IF; + if users is not null then + foreach userUuid in array users.useruUids + loop + call grantRoleToUser(roleUuid, userUuid); + end loop; + end if; - RETURN roleUuid; -END; $$; + return roleUuid; +end; $$; -CREATE OR REPLACE FUNCTION createRole( +create or replace function createRole( roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, users RbacUsers = null ) - RETURNS uuid - CALLED ON NULL INPUT - LANGUAGE plpgsql AS $$ -BEGIN - RETURN createRole(roleDescriptor, permissions, null, null, users); -END; $$; + returns uuid + called on null input + language plpgsql as $$ +begin + return createRole(roleDescriptor, permissions, null, null, users); +end; $$; -CREATE OR REPLACE FUNCTION createRole( +create or replace function createRole( roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, subRoles RbacSubRoles, users RbacUsers = null ) - RETURNS uuid - CALLED ON NULL INPUT - LANGUAGE plpgsql AS $$ -BEGIN - RETURN createRole(roleDescriptor, permissions, null, subRoles, users); -END; $$; + returns uuid + called on null input + language plpgsql as $$ +begin + return createRole(roleDescriptor, permissions, null, subRoles, users); +end; $$; --// diff --git a/src/main/resources/db/changelog/2022-07-28-030-rbac-statistics.sql b/src/main/resources/db/changelog/2022-07-28-030-rbac-statistics.sql index 94d8a2d3..1ef6283a 100644 --- a/src/main/resources/db/changelog/2022-07-28-030-rbac-statistics.sql +++ b/src/main/resources/db/changelog/2022-07-28-030-rbac-statistics.sql @@ -5,20 +5,24 @@ /* Creates a view which presents some statistics about the RBAC tables. */ -create view RbacStatisticsView AS - select no, to_char("count", '9 999 999 999') as "count", "table" - from ( - select 1 as no, count(*) as "count", 'login users' as "table" from RbacUser - union - select 2 as no, count(*) as "count", 'roles' as "table" from RbacRole - union - select 3 as no, count(*) as "count", 'permissions' as "table" from RbacPermission - union - select 4 as no, count(*) as "count", 'references' as "table" from RbacReference - union - select 5 as no, count(*) as "count", 'grants' as "table" from RbacGrants - union - select 6 as no, count(*) as "count", 'objects' as "table" from RbacObject - ) as totals +create view RbacStatisticsView as +select no, to_char("count", '9 999 999 999') as "count", "table" + from (select 1 as no, count(*) as "count", 'login users' as "table" + from RbacUser + union + select 2 as no, count(*) as "count", 'roles' as "table" + from RbacRole + union + select 3 as no, count(*) as "count", 'permissions' as "table" + from RbacPermission + union + select 4 as no, count(*) as "count", 'references' as "table" + from RbacReference + union + select 5 as no, count(*) as "count", 'grants' as "table" + from RbacGrants + union + select 6 as no, count(*) as "count", 'objects' as "table" + from RbacObject) as totals order by totals.no; --// diff --git a/src/main/resources/db/changelog/2022-07-28-051-hs-customer.sql b/src/main/resources/db/changelog/2022-07-28-051-hs-customer.sql new file mode 100644 index 00000000..831fadcf --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-28-051-hs-customer.sql @@ -0,0 +1,183 @@ +-- ======================================================== +-- Customer example with RBAC +-- -------------------------------------------------------- + +set session session authorization default; + +create table if not exists customer +( + uuid uuid unique references RbacObject (uuid), + reference int not null unique check (reference between 10000 and 99999), + prefix character(3) unique, + adminUserName varchar(63) +); + +drop trigger if exists createRbacObjectForCustomer_Trigger on customer; +create trigger createRbacObjectForCustomer_Trigger + before insert + on customer + for each row +execute procedure createRbacObject(); + +create or replace function customerOwner(customer customer) + returns RbacRoleDescriptor + language plpgsql + strict as $$ +begin + return roleDescriptor('customer', customer.uuid, 'owner'); +end; $$; + +create or replace function customerAdmin(customer customer) + returns RbacRoleDescriptor + language plpgsql + strict as $$ +begin + return roleDescriptor('customer', customer.uuid, 'admin'); +end; $$; + +create or replace function customerTenant(customer customer) + returns RbacRoleDescriptor + language plpgsql + strict as $$ +begin + return roleDescriptor('customer', customer.uuid, 'tenant'); +end; $$; + + +create or replace function createRbacRulesForCustomer() + returns trigger + language plpgsql + strict as $$ +declare + customerOwnerUuid uuid; + customerAdminUuid uuid; +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of TRIGGER AFTER INSERT'; + end if; + + -- the owner role with full access for Hostsharing administrators + customerOwnerUuid = createRole( + customerOwner(NEW), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), + beneathRole(hostsharingAdmin()) + ); + + -- the admin role for the customer's admins, who can view and add products + customerAdminUuid = createRole( + customerAdmin(NEW), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view', 'add-package']), + -- NO auto follow for customer owner to avoid exploding permissions for administrators + withUser(NEW.adminUserName, 'create') -- implicitly ignored if null + ); + + -- allow the customer owner role (thus administrators) to assume the customer admin role + call grantRoleToRole(customerAdminUuid, customerOwnerUuid, false); + + -- the tenant role which later can be used by owners+admins of sub-objects + perform createRole( + customerTenant(NEW), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']) + ); + + return NEW; +end; $$; + +drop trigger if exists createRbacRulesForCustomer_Trigger on customer; +create trigger createRbacRulesForCustomer_Trigger + after insert + on customer + for each row +execute procedure createRbacRulesForCustomer(); + +create or replace function deleteRbacRulesForCustomer() + returns trigger + language plpgsql + strict as $$ +declare + objectTable varchar = 'customer'; +begin + if TG_OP = 'DELETE' then + + -- delete the owner role (for admininstrators) + call deleteRole(findRoleId(objectTable || '#' || NEW.prefix || '.owner')); + + -- delete the customer admin role + call deleteRole(findRoleId(objectTable || '#' || NEW.prefix || '.admin')); + else + raise exception 'invalid usage of TRIGGER BEFORE DELETE'; + end if; +end; $$; + +drop trigger if exists deleteRbacRulesForCustomer_Trigger on customer; +create trigger deleteRbacRulesForCustomer_Trigger + before delete + on customer + for each row +execute procedure deleteRbacRulesForCustomer(); + +-- create a restricted view to access the textual customer ids a idName +set session session authorization default; +-- ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +drop view if exists customer_iv; +create or replace view customer_iv as +select distinct target.uuid, target.prefix as idName + from customer as target; +-- TODO: Is it ok that everybody has access to this information? +grant all privileges on customer_iv to restricted; + +create or replace function customerUuidByIdName(idName varchar) + returns uuid + language sql + strict as $$ +select uuid from customer_iv iv where iv.idName = customerUuidByIdName.idName; +$$; + +-- create RBAC restricted view +set session session authorization default; +-- ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +drop view if exists customer_rv; +create or replace view customer_rv as +select distinct target.* + from customer as target + where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'customer', currentSubjectIds())); +grant all privileges on customer_rv to restricted; + + +-- generate Customer test data + +set session session authorization default; +do language plpgsql $$ + declare + currentTask varchar; + custReference integer; + custRowId uuid; + custPrefix varchar; + custAdminName varchar; + begin + set hsadminng.currentUser to ''; + + for t in 0..9 + loop + currentTask = 'creating RBAC test customer #' || t; + set local hsadminng.currentUser to 'mike@hostsharing.net'; + set local hsadminng.assumedRoles = ''; + set local hsadminng.currentTask to currentTask; + + -- When a new customer is created, + custReference = 10000 + t; + custRowId = uuid_generate_v4(); + custPrefix = intToVarChar(t, 3); + custAdminName = 'admin@' || custPrefix || '.example.com'; + + raise notice 'creating customer %:%', custReference, custPrefix; + insert + into customer (reference, prefix, adminUserName) + values (custReference, custPrefix, custAdminName); + + commit; + + end loop; + + end; +$$; diff --git a/src/main/resources/db/changelog/21-hs-customer.sql b/src/main/resources/db/changelog/21-hs-customer.sql deleted file mode 100644 index a386bc32..00000000 --- a/src/main/resources/db/changelog/21-hs-customer.sql +++ /dev/null @@ -1,169 +0,0 @@ - --- ======================================================== --- Customer example with RBAC --- -------------------------------------------------------- - -SET SESSION SESSION AUTHORIZATION DEFAULT ; - -CREATE TABLE IF NOT EXISTS customer ( - uuid uuid UNIQUE REFERENCES RbacObject(uuid), - reference int not null unique CHECK (reference BETWEEN 10000 AND 99999), - prefix character(3) unique, - adminUserName varchar(63) -); - -DROP TRIGGER IF EXISTS createRbacObjectForCustomer_Trigger ON customer; -CREATE TRIGGER createRbacObjectForCustomer_Trigger - BEFORE INSERT ON customer - FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); - -CREATE OR REPLACE FUNCTION customerOwner(customer customer) - RETURNS RbacRoleDescriptor - LANGUAGE plpgsql STRICT AS $$ -begin - return roleDescriptor('customer', customer.uuid, 'owner'); -end; $$; - -CREATE OR REPLACE FUNCTION customerAdmin(customer customer) - RETURNS RbacRoleDescriptor - LANGUAGE plpgsql STRICT AS $$ -begin - return roleDescriptor('customer', customer.uuid, 'admin'); -end; $$; - -CREATE OR REPLACE FUNCTION customerTenant(customer customer) - RETURNS RbacRoleDescriptor - LANGUAGE plpgsql STRICT AS $$ -begin - return roleDescriptor('customer', customer.uuid, 'tenant'); -end; $$; - - -CREATE OR REPLACE FUNCTION createRbacRulesForCustomer() - RETURNS trigger - LANGUAGE plpgsql STRICT AS $$ -DECLARE - customerOwnerUuid uuid; - customerAdminUuid uuid; -BEGIN - IF TG_OP <> 'INSERT' THEN - RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; - END IF; - - -- the owner role with full access for Hostsharing administrators - customerOwnerUuid = createRole( - customerOwner(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(hostsharingAdmin()) - ); - - -- the admin role for the customer's admins, who can view and add products - customerAdminUuid = createRole( - customerAdmin(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view', 'add-package']), - -- NO auto follow for customer owner to avoid exploding permissions for administrators - withUser(NEW.adminUserName, 'create') -- implicitly ignored if null - ); - - -- allow the customer owner role (thus administrators) to assume the customer admin role - call grantRoleToRole(customerAdminUuid, customerOwnerUuid, FALSE); - - -- the tenant role which later can be used by owners+admins of sub-objects - perform createRole( - customerTenant(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view']) - ); - - RETURN NEW; -END; $$; - -DROP TRIGGER IF EXISTS createRbacRulesForCustomer_Trigger ON customer; -CREATE TRIGGER createRbacRulesForCustomer_Trigger - AFTER INSERT ON customer - FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForCustomer(); - -CREATE OR REPLACE FUNCTION deleteRbacRulesForCustomer() - RETURNS trigger - LANGUAGE plpgsql STRICT AS $$ -DECLARE - objectTable varchar = 'customer'; -BEGIN - IF TG_OP = 'DELETE' THEN - - -- delete the owner role (for admininstrators) - call deleteRole(findRoleId(objectTable||'#'||NEW.prefix||'.owner')); - - -- delete the customer admin role - call deleteRole(findRoleId(objectTable||'#'||NEW.prefix||'.admin')); - ELSE - RAISE EXCEPTION 'invalid usage of TRIGGER BEFORE DELETE'; - END IF; -END; $$; - -DROP TRIGGER IF EXISTS deleteRbacRulesForCustomer_Trigger ON customer; -CREATE TRIGGER deleteRbacRulesForCustomer_Trigger - BEFORE DELETE ON customer - FOR EACH ROW EXECUTE PROCEDURE deleteRbacRulesForCustomer(); - --- create a restricted view to access the textual customer ids a idName -SET SESSION SESSION AUTHORIZATION DEFAULT; --- ALTER TABLE customer ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS customer_iv; -CREATE OR REPLACE VIEW customer_iv AS -SELECT DISTINCT target.uuid, target.prefix as idName - FROM customer AS target; --- TODO: Is it ok that everybody has access to this information? -GRANT ALL PRIVILEGES ON customer_iv TO restricted; - -CREATE OR REPLACE FUNCTION customerUuidByIdName(idName varchar) - RETURNS uuid - LANGUAGE sql STRICT AS $$ - SELECT uuid FROM customer_iv iv WHERE iv.idName=customerUuidByIdName.idName; - $$; - --- create RBAC restricted view -SET SESSION SESSION AUTHORIZATION DEFAULT; --- ALTER TABLE customer ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS customer_rv; -CREATE OR REPLACE VIEW customer_rv AS - SELECT DISTINCT target.* - FROM customer AS target - WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'customer', currentSubjectIds())); -GRANT ALL PRIVILEGES ON customer_rv TO restricted; - - --- generate Customer test data - -SET SESSION SESSION AUTHORIZATION DEFAULT; -DO LANGUAGE plpgsql $$ - DECLARE - currentTask varchar; - custReference integer; - custRowId uuid; - custPrefix varchar; - custAdminName varchar; - BEGIN - SET hsadminng.currentUser TO ''; - - FOR t IN 0..9 LOOP - currentTask = 'creating RBAC test customer #' || t; - SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net'; - SET LOCAL hsadminng.assumedRoles = ''; - SET LOCAL hsadminng.currentTask TO currentTask; - - -- When a new customer is created, - custReference = 10000 + t; - custRowId = uuid_generate_v4(); - custPrefix = intToVarChar(t, 3 ); - custAdminName = 'admin@' || custPrefix || '.example.com'; - - raise notice 'creating customer %:%', custReference, custPrefix; - insert into customer (reference, prefix, adminUserName) - VALUES (custReference, custPrefix, custAdminName); - - COMMIT; - - END LOOP; - - END; -$$; diff --git a/src/main/resources/db/changelog/22-hs-packages.sql b/src/main/resources/db/changelog/22-hs-packages.sql index 65eed84a..91d91cc9 100644 --- a/src/main/resources/db/changelog/22-hs-packages.sql +++ b/src/main/resources/db/changelog/22-hs-packages.sql @@ -1,149 +1,161 @@ - -- ======================================================== -- Package example with RBAC -- -------------------------------------------------------- -SET SESSION SESSION AUTHORIZATION DEFAULT ; +set session session authorization default; -CREATE TABLE IF NOT EXISTS package ( - uuid uuid UNIQUE REFERENCES RbacObject(uuid), - name character varying(5), - customerUuid uuid REFERENCES customer(uuid) +create table if not exists package +( + uuid uuid unique references RbacObject (uuid), + name character varying(5), + customerUuid uuid references customer (uuid) ); -CREATE OR REPLACE FUNCTION packageOwner(pac package) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function packageOwner(pac package) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ declare roleDesc RbacRoleDescriptor; begin return roleDescriptor('package', pac.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION packageAdmin(pac package) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function packageAdmin(pac package) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('package', pac.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION packageTenant(pac package) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function packageTenant(pac package) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('package', pac.uuid, 'tenant'); end; $$; -DROP TRIGGER IF EXISTS createRbacObjectForPackage_Trigger ON package; -CREATE TRIGGER createRbacObjectForPackage_Trigger - BEFORE INSERT ON package - FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); +drop trigger if exists createRbacObjectForPackage_Trigger on package; +create trigger createRbacObjectForPackage_Trigger + before insert + on package + for each row +execute procedure createRbacObject(); -CREATE OR REPLACE FUNCTION createRbacRulesForPackage() - RETURNS trigger - LANGUAGE plpgsql STRICT AS $$ -DECLARE - parentCustomer customer; +create or replace function createRbacRulesForPackage() + returns trigger + language plpgsql + strict as $$ +declare + parentCustomer customer; packageOwnerRoleUuid uuid; packageAdminRoleUuid uuid; -BEGIN - IF TG_OP <> 'INSERT' THEN - RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; - END IF; +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of TRIGGER AFTER INSERT'; + end if; - SELECT * FROM customer AS c WHERE c.uuid=NEW.customerUuid INTO parentCustomer; + select * from customer as c where c.uuid = NEW.customerUuid into parentCustomer; -- an owner role is created and assigned to the customer's admin role packageOwnerRoleUuid = createRole( packageOwner(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), beneathRole(customerAdmin(parentCustomer)) ); -- an owner role is created and assigned to the package owner role packageAdminRoleUuid = createRole( packageAdmin(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-unixuser', 'add-domain']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit', 'add-unixuser', 'add-domain']), beneathRole(packageOwnerRoleUuid) ); -- and a package tenant role is created and assigned to the package admin as well perform createRole( packageTenant(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY ['view']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']), beneathRole(packageAdminRoleUuid), beingItselfA(customerTenant(parentCustomer)) ); - RETURN NEW; -END; $$; + return NEW; +end; $$; -DROP TRIGGER IF EXISTS createRbacRulesForPackage_Trigger ON package; -CREATE TRIGGER createRbacRulesForPackage_Trigger - AFTER INSERT ON package - FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForPackage(); +drop trigger if exists createRbacRulesForPackage_Trigger on package; +create trigger createRbacRulesForPackage_Trigger + after insert + on package + for each row +execute procedure createRbacRulesForPackage(); -CREATE OR REPLACE FUNCTION deleteRbacRulesForPackage() - RETURNS trigger - LANGUAGE plpgsql STRICT AS $$ -BEGIN - IF TG_OP = 'DELETE' THEN +create or replace function deleteRbacRulesForPackage() + returns trigger + language plpgsql + strict as $$ +begin + if TG_OP = 'DELETE' then -- TODO - ELSE - RAISE EXCEPTION 'invalid usage of TRIGGER BEFORE DELETE'; - END IF; -END; $$; + else + raise exception 'invalid usage of TRIGGER BEFORE DELETE'; + end if; +end; $$; -DROP TRIGGER IF EXISTS deleteRbacRulesForPackage_Trigger ON customer; -CREATE TRIGGER deleteRbacRulesForPackage_Trigger - BEFORE DELETE ON customer - FOR EACH ROW EXECUTE PROCEDURE deleteRbacRulesForPackage(); +drop trigger if exists deleteRbacRulesForPackage_Trigger on customer; +create trigger deleteRbacRulesForPackage_Trigger + before delete + on customer + for each row +execute procedure deleteRbacRulesForPackage(); -- create RBAC-restricted view -SET SESSION SESSION AUTHORIZATION DEFAULT; +set session session authorization default; -- ALTER TABLE package ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS package_rv; -CREATE OR REPLACE VIEW package_rv AS - SELECT DISTINCT target.* - FROM package AS target - WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'package', currentSubjectIds())); -GRANT ALL PRIVILEGES ON package_rv TO restricted; +drop view if exists package_rv; +create or replace view package_rv as +select distinct target.* + from package as target + where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'package', currentSubjectIds())); +grant all privileges on package_rv to restricted; -- generate Package test data -DO LANGUAGE plpgsql $$ - DECLARE - cust customer; - pacName varchar; +do language plpgsql $$ + declare + cust customer; + pacName varchar; currentTask varchar; - custAdmin varchar; - BEGIN - SET hsadminng.currentUser TO ''; + custAdmin varchar; + begin + set hsadminng.currentUser to ''; - FOR cust IN (SELECT * FROM customer) LOOP - -- CONTINUE WHEN cust.reference < 18000; + for cust in (select * from customer) + loop + -- CONTINUE WHEN cust.reference < 18000; - FOR t IN 0..randominrange(1, 2) LOOP - pacName = cust.prefix || TO_CHAR(t, 'fm00'); - currentTask = 'creating RBAC test package #'|| pacName || ' for customer ' || cust.prefix || ' #' || cust.uuid; - RAISE NOTICE 'task: %', currentTask; + for t in 0..randominrange(1, 2) + loop + pacName = cust.prefix || to_char(t, 'fm00'); + currentTask = 'creating RBAC test package #' || pacName || ' for customer ' || cust.prefix || ' #' || + cust.uuid; + raise notice 'task: %', currentTask; - custAdmin = 'admin@' || cust.prefix || '.example.com'; - SET LOCAL hsadminng.currentUser TO custAdmin; - SET LOCAL hsadminng.assumedRoles = ''; - SET LOCAL hsadminng.currentTask TO currentTask; + custAdmin = 'admin@' || cust.prefix || '.example.com'; + set local hsadminng.currentUser to custAdmin; + set local hsadminng.assumedRoles = ''; + set local hsadminng.currentTask to currentTask; - insert into package (name, customerUuid) - VALUES (pacName, cust.uuid); + insert + into package (name, customerUuid) + values (pacName, cust.uuid); - COMMIT; - END LOOP; - END LOOP; - END; + commit; + end loop; + end loop; + end; $$; diff --git a/src/main/resources/db/changelog/23-hs-unixuser.sql b/src/main/resources/db/changelog/23-hs-unixuser.sql index 00773135..98e640e0 100644 --- a/src/main/resources/db/changelog/23-hs-unixuser.sql +++ b/src/main/resources/db/changelog/23-hs-unixuser.sql @@ -1,152 +1,159 @@ - -- ======================================================== -- UnixUser example with RBAC -- -------------------------------------------------------- -SET SESSION SESSION AUTHORIZATION DEFAULT ; +set session session authorization default; -CREATE TABLE IF NOT EXISTS UnixUser ( - uuid uuid UNIQUE REFERENCES RbacObject(uuid), - name character varying(32), - comment character varying(96), - packageUuid uuid REFERENCES package(uuid) +create table if not exists UnixUser +( + uuid uuid unique references RbacObject (uuid), + name character varying(32), + comment character varying(96), + packageUuid uuid references package (uuid) ); -CREATE OR REPLACE FUNCTION unixUserOwner(uu UnixUser) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function unixUserOwner(uu UnixUser) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('unixuser', uu.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION unixUserAdmin(uu UnixUser) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function unixUserAdmin(uu UnixUser) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('unixuser', uu.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION unixUserTenant(uu UnixUser) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function unixUserTenant(uu UnixUser) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('unixuser', uu.uuid, 'tenant'); end; $$; -CREATE OR REPLACE FUNCTION createUnixUserTenantRoleIfNotExists(unixUser UnixUser) - RETURNS uuid - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ -DECLARE +create or replace function createUnixUserTenantRoleIfNotExists(unixUser UnixUser) + returns uuid + returns null on null input + language plpgsql as $$ +declare unixUserTenantRoleDesc RbacRoleDescriptor; unixUserTenantRoleUuid uuid; -BEGIN +begin unixUserTenantRoleDesc = unixUserTenant(unixUser); unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleDesc); - IF unixUserTenantRoleUuid IS NOT NULL THEN - RETURN unixUserTenantRoleUuid; - END IF; + if unixUserTenantRoleUuid is not null then + return unixUserTenantRoleUuid; + end if; - RETURN createRole( + return createRole( unixUserTenantRoleDesc, - grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => ARRAY['view']), + grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => array ['view']), beneathRole(unixUserAdmin(unixUser)) ); -END; $$; +end; $$; -DROP TRIGGER IF EXISTS createRbacObjectForUnixUser_Trigger ON UnixUser; -CREATE TRIGGER createRbacObjectForUnixUser_Trigger - BEFORE INSERT ON UnixUser - FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); +drop trigger if exists createRbacObjectForUnixUser_Trigger on UnixUser; +create trigger createRbacObjectForUnixUser_Trigger + before insert + on UnixUser + for each row +execute procedure createRbacObject(); -CREATE OR REPLACE FUNCTION createRbacRulesForUnixUser() - RETURNS trigger - LANGUAGE plpgsql STRICT AS $$ -DECLARE - parentPackage package; +create or replace function createRbacRulesForUnixUser() + returns trigger + language plpgsql + strict as $$ +declare + parentPackage package; unixuserOwnerRoleId uuid; unixuserAdminRoleId uuid; -BEGIN - IF TG_OP <> 'INSERT' THEN - RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; - END IF; +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of TRIGGER AFTER INSERT'; + end if; - SELECT * FROM package WHERE uuid=NEW.packageUuid into parentPackage; + select * from package where uuid = NEW.packageUuid into parentPackage; -- an owner role is created and assigned to the package's admin group unixuserOwnerRoleId = createRole( unixUserOwner(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), beneathRole(packageAdmin(parentPackage)) ); -- and a unixuser admin role is created and assigned to the unixuser owner as well unixuserAdminRoleId = createRole( unixUserAdmin(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']), beneathRole(unixuserOwnerRoleId), beingItselfA(packageTenant(parentPackage)) ); -- a tenent role is only created on demand - RETURN NEW; -END; $$; + return NEW; +end; $$; -DROP TRIGGER IF EXISTS createRbacRulesForUnixUser_Trigger ON UnixUser; -CREATE TRIGGER createRbacRulesForUnixUser_Trigger - AFTER INSERT ON UnixUser - FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForUnixUser(); +drop trigger if exists createRbacRulesForUnixUser_Trigger on UnixUser; +create trigger createRbacRulesForUnixUser_Trigger + after insert + on UnixUser + for each row +execute procedure createRbacRulesForUnixUser(); -- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForUnixUser() -- create RBAC-restricted view -SET SESSION SESSION AUTHORIZATION DEFAULT; +set session session authorization default; -- ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS unixuser_rv; -CREATE OR REPLACE VIEW unixuser_rv AS - SELECT DISTINCT target.* - FROM unixuser AS target - WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'unixuser', currentSubjectIds())); -GRANT ALL PRIVILEGES ON unixuser_rv TO restricted; +drop view if exists unixuser_rv; +create or replace view unixuser_rv as +select distinct target.* + from unixuser as target + where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'unixuser', currentSubjectIds())); +grant all privileges on unixuser_rv to restricted; -- generate UnixUser test data -DO LANGUAGE plpgsql $$ - DECLARE - pac record; - pacAdmin varchar; +do language plpgsql $$ + declare + pac record; + pacAdmin varchar; currentTask varchar; - BEGIN - SET hsadminng.currentUser TO ''; + begin + set hsadminng.currentUser to ''; - FOR pac IN ( - SELECT p.uuid, p.name - FROM package p - JOIN customer c ON p.customeruuid = c.uuid - -- WHERE c.reference >= 18000 - ) LOOP + for pac in (select p.uuid, p.name + from package p + join customer c on p.customeruuid = c.uuid + -- WHERE c.reference >= 18000 + ) + loop - FOR t IN 0..9 LOOP - currentTask = 'creating RBAC test unixuser #' || t || ' for package ' || pac.name|| ' #' || pac.uuid; - RAISE NOTICE 'task: %', currentTask; - pacAdmin = 'admin@' || pac.name || '.example.com'; - SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net'; -- TODO: use a package-admin - SET LOCAL hsadminng.assumedRoles = ''; - SET LOCAL hsadminng.currentTask TO currentTask; + for t in 0..9 + loop + currentTask = 'creating RBAC test unixuser #' || t || ' for package ' || pac.name || ' #' || pac.uuid; + raise notice 'task: %', currentTask; + pacAdmin = 'admin@' || pac.name || '.example.com'; + set local hsadminng.currentUser to 'mike@hostsharing.net'; -- TODO: use a package-admin + set local hsadminng.assumedRoles = ''; + set local hsadminng.currentTask to currentTask; - INSERT INTO unixuser (name, packageUuid) - VALUES (pac.name||'-'|| intToVarChar(t, 4), pac.uuid); + insert + into unixuser (name, packageUuid) + values (pac.name || '-' || intToVarChar(t, 4), pac.uuid); - COMMIT; - END LOOP; - END LOOP; + commit; + end loop; + end loop; - END; + end; $$; diff --git a/src/main/resources/db/changelog/24-hs-domain.sql b/src/main/resources/db/changelog/24-hs-domain.sql index 5435b35b..ac7d7205 100644 --- a/src/main/resources/db/changelog/24-hs-domain.sql +++ b/src/main/resources/db/changelog/24-hs-domain.sql @@ -1,144 +1,151 @@ - -- ======================================================== -- Domain example with RBAC -- -------------------------------------------------------- -SET SESSION SESSION AUTHORIZATION DEFAULT ; +set session session authorization default; -CREATE TABLE IF NOT EXISTS Domain ( - uuid uuid UNIQUE REFERENCES RbacObject(uuid), - name character varying(32), - unixUserUuid uuid REFERENCES unixuser(uuid) +create table if not exists Domain +( + uuid uuid unique references RbacObject (uuid), + name character varying(32), + unixUserUuid uuid references unixuser (uuid) ); -DROP TRIGGER IF EXISTS createRbacObjectForDomain_Trigger ON Domain; -CREATE TRIGGER createRbacObjectForDomain_Trigger - BEFORE INSERT ON Domain - FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); +drop trigger if exists createRbacObjectForDomain_Trigger on Domain; +create trigger createRbacObjectForDomain_Trigger + before insert + on Domain + for each row +execute procedure createRbacObject(); -CREATE OR REPLACE FUNCTION domainOwner(dom Domain) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function domainOwner(dom Domain) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('domain', dom.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION domainAdmin(dom Domain) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function domainAdmin(dom Domain) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('domain', dom.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION domainTenant(dom Domain) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function domainTenant(dom Domain) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('domain', dom.uuid, 'tenant'); end; $$; -CREATE OR REPLACE FUNCTION createRbacRulesForDomain() - RETURNS trigger - LANGUAGE plpgsql STRICT AS $$ -DECLARE - parentUser UnixUser; - parentPackage package; +create or replace function createRbacRulesForDomain() + returns trigger + language plpgsql + strict as $$ +declare + parentUser UnixUser; + parentPackage package; domainOwnerRoleUuid uuid; domainAdminRoleUuid uuid; -BEGIN - IF TG_OP <> 'INSERT' THEN - RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; - END IF; +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of TRIGGER AFTER INSERT'; + end if; - SELECT * FROM UnixUser WHERE uuid=NEW.unixUserUuid into parentUser; - SELECT * FROM Package WHERE uuid=parentUser.packageuuid into parentPackage; + select * from UnixUser where uuid = NEW.unixUserUuid into parentUser; + select * from Package where uuid = parentUser.packageuuid into parentPackage; -- a domain owner role is created and assigned to the unixuser's admin role domainOwnerRoleUuid = createRole( domainOwner(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), beneathRole(packageAdmin(parentPackage)) ); -- a domain admin role is created and assigned to the domain's owner role domainAdminRoleUuid = createRole( domainAdmin(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-emailaddress']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit', 'add-emailaddress']), beneathRole(domainOwnerRoleUuid) ); -- and a domain tenant role is created and assigned to the domain's admiin role perform createRole( domainTenant(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), beneathRole(domainAdminRoleUuid), beingItselfA(createUnixUserTenantRoleIfNotExists(parentUser)) ); - RETURN NEW; -END; $$; + return NEW; +end; $$; -DROP TRIGGER IF EXISTS createRbacRulesForDomain_Trigger ON Domain; -CREATE TRIGGER createRbacRulesForDomain_Trigger - AFTER INSERT ON Domain - FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForDomain(); +drop trigger if exists createRbacRulesForDomain_Trigger on Domain; +create trigger createRbacRulesForDomain_Trigger + after insert + on Domain + for each row +execute procedure createRbacRulesForDomain(); -- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForDomain() -- create RBAC-restricted view -SET SESSION SESSION AUTHORIZATION DEFAULT; +set session session authorization default; -- ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS domain_rv; -CREATE OR REPLACE VIEW domain_rv AS - SELECT DISTINCT target.* - FROM Domain AS target - WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'domain', currentSubjectIds())); -GRANT ALL PRIVILEGES ON domain_rv TO restricted; +drop view if exists domain_rv; +create or replace view domain_rv as +select distinct target.* + from Domain as target + where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'domain', currentSubjectIds())); +grant all privileges on domain_rv to restricted; -- generate Domain test data -DO LANGUAGE plpgsql $$ - DECLARE - uu record; - pac package; - pacAdmin varchar; +do language plpgsql $$ + declare + uu record; + pac package; + pacAdmin varchar; currentTask varchar; - BEGIN - SET hsadminng.currentUser TO ''; + begin + set hsadminng.currentUser to ''; - FOR uu IN ( - SELECT u.uuid, u.name, u.packageuuid, c.reference - FROM unixuser u - JOIN package p ON u.packageuuid = p.uuid - JOIN customer c ON p.customeruuid = c.uuid - -- WHERE c.reference >= 18000 - ) LOOP - IF ( random() < 0.3 ) THEN - FOR t IN 0..1 LOOP - currentTask = 'creating RBAC test Domain #' || t || ' for UnixUser ' || uu.name|| ' #' || uu.uuid; - RAISE NOTICE 'task: %', currentTask; + for uu in (select u.uuid, u.name, u.packageuuid, c.reference + from unixuser u + join package p on u.packageuuid = p.uuid + join customer c on p.customeruuid = c.uuid + -- WHERE c.reference >= 18000 + ) + loop + if (random() < 0.3) then + for t in 0..1 + loop + currentTask = 'creating RBAC test Domain #' || t || ' for UnixUser ' || uu.name || ' #' || uu.uuid; + raise notice 'task: %', currentTask; - SELECT * FROM package WHERE uuid=uu.packageUuid INTO pac; - pacAdmin = 'admin@' || pac.name || '.example.com'; - SET LOCAL hsadminng.currentUser TO pacAdmin; - SET LOCAL hsadminng.assumedRoles = ''; - SET LOCAL hsadminng.currentTask TO currentTask; + select * from package where uuid = uu.packageUuid into pac; + pacAdmin = 'admin@' || pac.name || '.example.com'; + set local hsadminng.currentUser to pacAdmin; + set local hsadminng.assumedRoles = ''; + set local hsadminng.currentTask to currentTask; - INSERT INTO Domain (name, unixUserUuid) - VALUES ('dom-' || t || '.' || uu.name || '.example.org' , uu.uuid); + insert + into Domain (name, unixUserUuid) + values ('dom-' || t || '.' || uu.name || '.example.org', uu.uuid); - COMMIT; - END LOOP; - END IF; - END LOOP; + commit; + end loop; + end if; + end loop; - END; + end; $$; diff --git a/src/main/resources/db/changelog/25-hs-emailaddress.sql b/src/main/resources/db/changelog/25-hs-emailaddress.sql index 80cdb759..97b884a7 100644 --- a/src/main/resources/db/changelog/25-hs-emailaddress.sql +++ b/src/main/resources/db/changelog/25-hs-emailaddress.sql @@ -1,123 +1,131 @@ - -- ======================================================== -- EMailAddress example with RBAC -- -------------------------------------------------------- -SET SESSION SESSION AUTHORIZATION DEFAULT ; +set session session authorization default; -CREATE TABLE IF NOT EXISTS EMailAddress ( - uuid uuid UNIQUE REFERENCES RbacObject(uuid), - localPart character varying(64), - domainUuid uuid REFERENCES domain(uuid) +create table if not exists EMailAddress +( + uuid uuid unique references RbacObject (uuid), + localPart character varying(64), + domainUuid uuid references domain (uuid) ); -DROP TRIGGER IF EXISTS createRbacObjectForEMailAddress_Trigger ON EMailAddress; -CREATE TRIGGER createRbacObjectForEMailAddress_Trigger - BEFORE INSERT ON EMailAddress - FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); +drop trigger if exists createRbacObjectForEMailAddress_Trigger on EMailAddress; +create trigger createRbacObjectForEMailAddress_Trigger + before insert + on EMailAddress + for each row +execute procedure createRbacObject(); -CREATE OR REPLACE FUNCTION emailAddressOwner(emAddr EMailAddress) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function emailAddressOwner(emAddr EMailAddress) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('emailaddress', emAddr.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION emailAddressAdmin(emAddr EMailAddress) - RETURNS RbacRoleDescriptor - RETURNS NULL ON NULL INPUT - LANGUAGE plpgsql AS $$ +create or replace function emailAddressAdmin(emAddr EMailAddress) + returns RbacRoleDescriptor + returns null on null input + language plpgsql as $$ begin return roleDescriptor('emailaddress', emAddr.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION createRbacRulesForEMailAddress() - RETURNS trigger - LANGUAGE plpgsql STRICT AS $$ -DECLARE +create or replace function createRbacRulesForEMailAddress() + returns trigger + language plpgsql + strict as $$ +declare parentDomain Domain; eMailAddressOwnerRoleUuid uuid; -BEGIN - IF TG_OP <> 'INSERT' THEN - RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; - END IF; +begin + if TG_OP <> 'INSERT' then + raise exception 'invalid usage of TRIGGER AFTER INSERT'; + end if; - SELECT d.* - FROM domain d - LEFT JOIN unixuser u ON u.uuid = d.unixuseruuid - WHERE d.uuid=NEW.domainUuid INTO parentDomain; + select d.* + from domain d + left join unixuser u on u.uuid = d.unixuseruuid + where d.uuid = NEW.domainUuid + into parentDomain; -- an owner role is created and assigned to the domains's admin group eMailAddressOwnerRoleUuid = createRole( emailAddressOwner(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(domainAdmin( parentDomain)) + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), + beneathRole(domainAdmin(parentDomain)) ); -- and an admin role is created and assigned to the unixuser owner as well perform createRole( emailAddressAdmin(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']), beneathRole(eMailAddressOwnerRoleUuid), beingItselfA(domainTenant(parentDomain)) ); - RETURN NEW; -END; $$; + return NEW; +end; $$; -DROP TRIGGER IF EXISTS createRbacRulesForEMailAddress_Trigger ON EMailAddress; -CREATE TRIGGER createRbacRulesForEMailAddress_Trigger - AFTER INSERT ON EMailAddress - FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForEMailAddress(); +drop trigger if exists createRbacRulesForEMailAddress_Trigger on EMailAddress; +create trigger createRbacRulesForEMailAddress_Trigger + after insert + on EMailAddress + for each row +execute procedure createRbacRulesForEMailAddress(); -- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForEMailAddress() -- create RBAC-restricted view -SET SESSION SESSION AUTHORIZATION DEFAULT; +set session session authorization default; -- ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; -DROP VIEW IF EXISTS EMailAddress_rv; -CREATE OR REPLACE VIEW EMailAddress_rv AS - SELECT DISTINCT target.* - FROM EMailAddress AS target - WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'emailaddress', currentSubjectIds())); -GRANT ALL PRIVILEGES ON EMailAddress_rv TO restricted; +drop view if exists EMailAddress_rv; +create or replace view EMailAddress_rv as +select distinct target.* + from EMailAddress as target + where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'emailaddress', currentSubjectIds())); +grant all privileges on EMailAddress_rv to restricted; -- generate EMailAddress test data -DO LANGUAGE plpgsql $$ - DECLARE - dom record; - pacAdmin varchar; +do language plpgsql $$ + declare + dom record; + pacAdmin varchar; currentTask varchar; - BEGIN - SET hsadminng.currentUser TO ''; + begin + set hsadminng.currentUser to ''; - FOR dom IN ( - SELECT d.uuid, d.name, p.name as packageName - FROM domain d - JOIN unixuser u ON u.uuid = d.unixuseruuid - JOIN package p ON u.packageuuid = p.uuid - JOIN customer c ON p.customeruuid = c.uuid - -- WHERE c.reference >= 18000 - ) LOOP - FOR t IN 0..4 LOOP - currentTask = 'creating RBAC test EMailAddress #' || t || ' for Domain ' || dom.name; - RAISE NOTICE 'task: %', currentTask; + for dom in (select d.uuid, d.name, p.name as packageName + from domain d + join unixuser u on u.uuid = d.unixuseruuid + join package p on u.packageuuid = p.uuid + join customer c on p.customeruuid = c.uuid + -- WHERE c.reference >= 18000 + ) + loop + for t in 0..4 + loop + currentTask = 'creating RBAC test EMailAddress #' || t || ' for Domain ' || dom.name; + raise notice 'task: %', currentTask; - pacAdmin = 'admin@' || dom.packageName || '.example.com'; - SET LOCAL hsadminng.currentUser TO pacAdmin; - SET LOCAL hsadminng.assumedRoles = ''; - SET LOCAL hsadminng.currentTask TO currentTask; + pacAdmin = 'admin@' || dom.packageName || '.example.com'; + set local hsadminng.currentUser to pacAdmin; + set local hsadminng.assumedRoles = ''; + set local hsadminng.currentTask to currentTask; - INSERT INTO EMailAddress (localPart, domainUuid) - VALUES ('local' || t, dom.uuid); + insert + into EMailAddress (localPart, domainUuid) + values ('local' || t, dom.uuid); - COMMIT; - END LOOP; - END LOOP; - END; + commit; + end loop; + end loop; + end; $$; diff --git a/src/main/resources/db/changelog/29-hs-statistics.sql b/src/main/resources/db/changelog/29-hs-statistics.sql index d296a2d9..3dd43b5d 100644 --- a/src/main/resources/db/changelog/29-hs-statistics.sql +++ b/src/main/resources/db/changelog/29-hs-statistics.sql @@ -1,26 +1,28 @@ - -- ======================================================== -- Some Business Table Statistics -- -------------------------------------------------------- -DROP VIEW IF EXISTS "BusinessTableStatisticsV"; -CREATE VIEW "BusinessTableStatisticsV" AS -SELECT no, to_char("count", '999 999 999') as "count", to_char("required", '999 999 999') as "required", to_char("count"::float/"required"::float, '990.999') as "factor", "table" -FROM (select 1 as no, count(*) as "count", 7000 as "required", 'customers' as "table" - from customer - UNION - select 2 as no, count(*) as "count", 15000 as "required", 'packages' as "table" - from package - UNION - select 3 as no, count(*) as "count", 150000 as "required", 'unixuser' as "table" - from unixuser - UNION - select 4 as no, count(*) as "count", 100000 as "required", 'domain' as "table" - from domain - UNION - select 5 as no, count(*) as "count", 500000 as "required", 'emailaddress' as "table" - from emailaddress - ) totals -ORDER BY totals.no; +drop view if exists "BusinessTableStatisticsV"; +create view "BusinessTableStatisticsV" as +select no, + to_char("count", '999 999 999') as "count", + to_char("required", '999 999 999') as "required", + to_char("count"::float / "required"::float, '990.999') as "factor", + "table" + from (select 1 as no, count(*) as "count", 7000 as "required", 'customers' as "table" + from customer + union + select 2 as no, count(*) as "count", 15000 as "required", 'packages' as "table" + from package + union + select 3 as no, count(*) as "count", 150000 as "required", 'unixuser' as "table" + from unixuser + union + select 4 as no, count(*) as "count", 100000 as "required", 'domain' as "table" + from domain + union + select 5 as no, count(*) as "count", 500000 as "required", 'emailaddress' as "table" + from emailaddress) totals + order by totals.no; -SELECT * FROM "BusinessTableStatisticsV"; +select * from "BusinessTableStatisticsV"; From 9beaf5e68461b24dc2ca2a4f1f9b26765a80349e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 09:03:21 +0200 Subject: [PATCH 21/54] improved README.md for first steps --- README.md | 73 ++++++++++++++++--- .../hostsharing/hsadminng/TestController.java | 14 ++-- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index cfcafc09..6c04e7d6 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,68 @@ ## Setting up the Development Environment +All instructions assume that you're using a current Linux operating system. +Everything is tested on Ubuntu Linux 22.04. + +To be able to build and run the Java Spring Boot application, you need the following tools: + +- Docker 20.x +- PostgreSQL Server 13.7-bullseye (see instructions below to install and run in Docker) +- Java JDK 17.x +- Gradle in some not too outdated version (7.4 will be installed via wrapper) + +You also might need an IDE (e.g. *IntelliJ IDEA* or *Eclipse* or *VS Code* with *[STS](https://spring.io/tools)* and a GUI Frontend for *PostgreSQL* like *Postbird*. + +If you have at least Docker, the Java JDK and Gradle installed in appropriate versions and in your `PATH`, then you can start like this: + + cd your-hsadmin-ng-directory + + gradle wrapper # downloads Gradle 7.5 into the project + source .alias # creates some comforable bash aliases, e.g. 'gw'='./gradlew' + + gw test # compiles and runs unit- and integration-tests + + pg-sql-run # downloads + runs PostgreSQL in a Docker container on localhost:5432 + gw bootRun # compiles and runs the application on localhost:8080 + + curl http://localhost:8080/api/ping # will reply with "pong" + curl http://localhost:8080/api/currentUser # will set+retrieve a current user + +The latter `curl` command actually goes through the database server. + +If you still need to install some of these tools, find some hints in the next chapters. + + +### SDKMAN + +*SdkMan* is not necessary, but helpful to install and switch between different versions of SDKs (Software-Development-Kits) and development tools in general, e.g. *JDK* and *Gradle*. + +You can get it from: https://sdkman.io/. + +**⚠** +Yeah, the `curl ... | bash` install method looks quite scary; +but in a development environment you're downloading executables all the time, +e.g. through `npm`, `Maven` or `Gradle` when downloading dependencies. +Thus, maybe you should at least use a separate Linux account for development. + +Once it's installed, you can install *JDK* and *Gradle*: + + sdk install java 17.0.3-tem + sdk install gradle + + sdk use java 17.0.3-tem # use this to switch between installed JDK versions + + ### PostgreSQL Server -So far the spike contains almost only PostgreSQL Code. -All you need so far, is a PostgreSQL database, for now with full admin rights. -The easiest way to set it up is using docker. +You could use any PostgreSQL Server (from version 13 on) installed on your machine. +You might amend the port and user settings in `src/main/resources/application.yml`, though. -(Find the mentioned aliases in `.aliases`.) +But the easiest way to run PostgreSQL is via Docker. Initially, pull an image compatible to current PostgreSQL version of Hostsharing: - docker pull postgres:13.7-bullseye + docker pull postgres:13.7-bullseye **⚠** If we switch the version, please also amend the documentation as well as the aliases file. Thanks! @@ -117,17 +168,19 @@ If you have figured out how it works, please add instructions above this section ### For RBAC -If you run the numbered SQL files from the `sql` folder in the defined order, a working RBAC system is built up in the database including test data and some simple tests. +The Schema is automatically created via *Liquibase*, a database migration library. +Currently, also some test data is automatically created. -To increase the amount of test data, simply increase the number of generated customers in `21-hs-customer.sql`. +To increase the amount of test data, increase the number of generated customers in `2022-07-28-051-hs-customer.sql` and run that If you already have data, e.g. for customers 0..999 (thus with reference numbers 10000..10999) and want to add another 1000 customers, amend the for loop to 1000...1999 and also uncomment and amend the `CONTINUE WHEN` or `WHERE` conditions in the other test data generators, using the first new customer reference number (in the example that's 11000). ### For Historization -You can explore the historization prototype as follows: +The historization is not yet integrated into the *Liquibase*-scripts. +You can explore the prototype as follows: - start with an empty database (the example tables are currently not compatible with RBAC), -- then run `historization.sql,` -- finally run `examples.sql`. +- then run `historization.sql` in the database, +- finally run `examples.sql` in the database. diff --git a/src/main/java/net/hostsharing/hsadminng/TestController.java b/src/main/java/net/hostsharing/hsadminng/TestController.java index cddadf00..fbd7f6ce 100644 --- a/src/main/java/net/hostsharing/hsadminng/TestController.java +++ b/src/main/java/net/hostsharing/hsadminng/TestController.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng; -import org.hibernate.type.PostgresUUIDType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -9,7 +8,6 @@ import org.springframework.web.bind.annotation.ResponseBody; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.transaction.Transactional; -import java.util.List; @Controller public class TestController { @@ -17,12 +15,10 @@ public class TestController { @PersistenceContext private EntityManager em; - @RequestMapping(value = "/api/test", method = RequestMethod.GET) - public List test() { - final var query = em.createNativeQuery("select * from public.rbacuser") - .unwrap(org.hibernate.query.NativeQuery.class) - .addScalar("uuidColumn", PostgresUUIDType.INSTANCE); - return query.getResultList(); + @ResponseBody + @RequestMapping(value = "/api/ping", method = RequestMethod.GET) + public String ping() { + return "pong\n"; } @Transactional @@ -32,6 +28,6 @@ public class TestController { em.createNativeQuery("SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';").executeUpdate(); em.createNativeQuery("SET LOCAL hsadminng.assumedRoles = '';").executeUpdate(); final var query = em.createNativeQuery("select currentUser()"); - return query.getSingleResult().toString(); + return query.getSingleResult() + "\n"; } } From 4edff1c2f0bfdb83a2f44fb109d9649ef210c8e7 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 09:20:18 +0200 Subject: [PATCH 22/54] move rbac.md to doc folder --- {sql => doc}/rbac.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {sql => doc}/rbac.md (100%) diff --git a/sql/rbac.md b/doc/rbac.md similarity index 100% rename from sql/rbac.md rename to doc/rbac.md From a478fe4cf1d5db5e361142f29547bb1d30560302 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 11:38:51 +0200 Subject: [PATCH 23/54] cleanup in Liquibase files, header, formatting etc. --- src/main/resources/application.yml | 3 + .../resources/db/changelog/20-hs-base.sql | 37 -------- .../db/changelog/2022-07-28-000-template.sql | 5 +- .../2022-07-28-001-last-row-count.sql | 4 +- .../changelog/2022-07-28-002-int-to-var.sql | 4 +- .../2022-07-28-003-random-in-range.sql | 5 +- .../2022-07-28-004-uuid-ossp-extension.sql | 5 +- .../db/changelog/2022-07-28-005-rbac-base.sql | 14 ++++ .../2022-07-28-020-rbac-role-builder.sql | 5 +- .../db/changelog/2022-07-29-050-hs-base.sql | 84 +++++++++++++++++++ .../db/changelog/db.changelog-master.yaml | 3 + 11 files changed, 124 insertions(+), 45 deletions(-) delete mode 100644 src/main/resources/db/changelog/20-hs-base.sql create mode 100644 src/main/resources/db/changelog/2022-07-29-050-hs-base.sql diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a1fead7f..5cd25fa5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -9,3 +9,6 @@ spring: sql: init: mode: never + +liquibase: + contexts: dev diff --git a/src/main/resources/db/changelog/20-hs-base.sql b/src/main/resources/db/changelog/20-hs-base.sql deleted file mode 100644 index b81aa4ae..00000000 --- a/src/main/resources/db/changelog/20-hs-base.sql +++ /dev/null @@ -1,37 +0,0 @@ -create table Hostsharing -( - uuid uuid primary key references RbacObject (uuid) -); -create unique index Hostsharing_Singleton on Hostsharing ((0)); - - -insert -into RbacObject (objecttable) values ('hostsharing'); -insert - into Hostsharing (uuid) values ((select uuid from RbacObject where objectTable = 'hostsharing')); - -create or replace function hostsharingAdmin() - returns RbacRoleDescriptor - returns null on null input - stable leakproof - language sql as $$ -select 'global', (select uuid from RbacObject where objectTable = 'hostsharing'), 'admin'::RbacRoleType; -$$; - --- create administrators role with two assigned users -do language plpgsql $$ - declare - admins uuid ; - begin - admins = createRole(hostsharingAdmin()); - call grantRoleToUser(admins, createRbacUser('mike@hostsharing.net')); - call grantRoleToUser(admins, createRbacUser('sven@hostsharing.net')); - commit; - end; -$$; - - -begin transaction; -set local hsadminng.currentUser = 'mike@hostsharing.net'; -select * from RbacUser where uuid = currentUserId(); -end transaction; diff --git a/src/main/resources/db/changelog/2022-07-28-000-template.sql b/src/main/resources/db/changelog/2022-07-28-000-template.sql index 04fe08fa..1dc12f42 100644 --- a/src/main/resources/db/changelog/2022-07-28-000-template.sql +++ b/src/main/resources/db/changelog/2022-07-28-000-template.sql @@ -1,6 +1,9 @@ --liquibase formatted sql ---changeset template:1 endDelimiter:--// + +-- ============================================================================ +--changeset prefix-TEMPLATE:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* diff --git a/src/main/resources/db/changelog/2022-07-28-001-last-row-count.sql b/src/main/resources/db/changelog/2022-07-28-001-last-row-count.sql index f6ca062f..c1f3fe05 100644 --- a/src/main/resources/db/changelog/2022-07-28-001-last-row-count.sql +++ b/src/main/resources/db/changelog/2022-07-28-001-last-row-count.sql @@ -1,7 +1,9 @@ --liquibase formatted sql +-- ============================================================================ +-- LAST-ROW-COUNT --changeset last-row-count:1 endDelimiter:--// - +-- ---------------------------------------------------------------------------- /* Returns the row count from the result of the previous query. Other than the native statement it's usable in an expression. diff --git a/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql b/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql index cccc037d..eb3212e3 100644 --- a/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql +++ b/src/main/resources/db/changelog/2022-07-28-002-int-to-var.sql @@ -1,7 +1,9 @@ --liquibase formatted sql +-- ============================================================================ +-- INT-TO-VAR --changeset int-to-var:1 endDelimiter:--// - +-- ---------------------------------------------------------------------------- /* Returns a textual representation of an integer number to be used as generated test data. diff --git a/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql b/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql index 41376770..8ed0112d 100644 --- a/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql +++ b/src/main/resources/db/changelog/2022-07-28-003-random-in-range.sql @@ -1,7 +1,10 @@ --liquibase formatted sql ---changeset random-in-range:1 endDelimiter:--// +-- ============================================================================ +-- RANDOM-IN-RANGE +--changeset random-in-range:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* Returns a random integer in the given range (both included), to be used for test data generation. diff --git a/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql b/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql index 45d6e799..f156af69 100644 --- a/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql +++ b/src/main/resources/db/changelog/2022-07-28-004-uuid-ossp-extension.sql @@ -1,7 +1,10 @@ --liquibase formatted sql ---changeset uuid-ossp-extension:1 endDelimiter:--// +-- ============================================================================ +-- UUID-OSSP-EXTENSION +--changeset uuid-ossp-extension:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* Makes improved uuid generation available. */ diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index 1a91f0d0..7006b0e3 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -639,3 +639,17 @@ begin return roleIdsToAssume; end; $$; --// + + +-- ============================================================================ +-- PGSQL-ROLES +--changeset rbac-base-pgsql-roles:1 endDelimiter:--// +-- ------------------------------------------------------------------ + +CREATE ROLE admin; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin; + +CREATE ROLE restricted; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; + +--// diff --git a/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql index 9079100b..2c71640b 100644 --- a/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql +++ b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql @@ -1,10 +1,9 @@ --liquibase formatted sql --- ================================================================== +-- ============================================================================ -- PERMISSIONS --changeset rbac-role-builder-permissions:1 endDelimiter:--// --- ------------------------------------------------------------------ - +-- ---------------------------------------------------------------------------- /* */ diff --git a/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql b/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql new file mode 100644 index 00000000..043a995d --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql @@ -0,0 +1,84 @@ +--liquibase formatted sql + +-- ============================================================================ +--changeset hs-base-GLOBAL-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + The purpose of this table is to contain a single row + which can be referenced from global roles as an object. + Otherwise these columns needed to be nullable and + many queries would be more complicated. + */ +create table Hostsharing +( + uuid uuid primary key references RbacObject (uuid) +); +create unique index Hostsharing_Singleton on Hostsharing ((0)); + +/** + A single row to be referenced as a global object. + */ +insert + into RbacObject (objecttable) values ('hostsharing'); +insert + into Hostsharing (uuid) values ((select uuid from RbacObject where objectTable = 'hostsharing')); +--// + +-- ============================================================================ +--changeset hs-base-ADMIN-ROLE:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + A global administrator role. + */ +create or replace function hostsharingAdmin() + returns RbacRoleDescriptor + returns null on null input + stable leakproof + language sql as $$ + select 'global', (select uuid from RbacObject where objectTable = 'hostsharing'), 'admin'::RbacRoleType; +$$; +select createRole(hostsharingAdmin()); + +-- ============================================================================ +--changeset hs-base-ADMIN-USERS:1 context:dev endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + Create two users and assign both to the administrators role. + */ +do language plpgsql $$ + declare + admins uuid ; + begin + admins = findRoleId(hostsharingAdmin()); + call grantRoleToUser(admins, createRbacUser('mike@hostsharing.net')); + call grantRoleToUser(admins, createRbacUser('sven@hostsharing.net')); + end; +$$; +--// + + +-- ============================================================================ +--changeset hs-base-hostsharing-TEST:1 context:dev runAlways:true endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Tests if currentUserId() can fetch the user from the session variable. + */ + +do language plpgsql $$ + declare + userName varchar; + begin + set local hsadminng.currentUser = 'mike@hostsharing.net'; + select userName from RbacUser where uuid = currentUserId() into userName; + if userName <> 'mike@hostsharing.net' then + raise exception 'fetching initial currentUser failed'; + end if; + + set local hsadminng.currentUser = 'sven@hostsharing.net'; + select userName from RbacUser where uuid = currentUserId() into userName; + if userName <> 'sven@hostsharing.net' then + raise exception 'fetching changed currentUser failed'; + end if; + end; $$; +--// diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index ae24da75..ccb1b7d2 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -13,4 +13,7 @@ databaseChangeLog: file: db/changelog/2022-07-28-020-rbac-role-builder.sql - include: file: db/changelog/2022-07-28-030-rbac-statistics.sql + - include: + file: db/changelog/2022-07-29-050-hs-base.sql + From 8ba952a41db3643f19f0762b5d6ac5394178315b Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 11:39:32 +0200 Subject: [PATCH 24/54] hs-customer SQL-Scripts in Liquibase --- .../changelog/2022-07-29-060-hs-customer.sql | 14 ++ ...ql => 2022-07-29-061-hs-customer-rbac.sql} | 133 +++++++++--------- .../2022-07-29-062-hs-customer-test-data.sql | 57 ++++++++ .../db/changelog/db.changelog-master.yaml | 6 + 4 files changed, 144 insertions(+), 66 deletions(-) create mode 100644 src/main/resources/db/changelog/2022-07-29-060-hs-customer.sql rename src/main/resources/db/changelog/{2022-07-28-051-hs-customer.sql => 2022-07-29-061-hs-customer-rbac.sql} (57%) create mode 100644 src/main/resources/db/changelog/2022-07-29-062-hs-customer-test-data.sql diff --git a/src/main/resources/db/changelog/2022-07-29-060-hs-customer.sql b/src/main/resources/db/changelog/2022-07-29-060-hs-customer.sql new file mode 100644 index 00000000..ace77ea6 --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-29-060-hs-customer.sql @@ -0,0 +1,14 @@ +--liquibase formatted sql + +-- ============================================================================ +--changeset hs-customer-MAIN-TABLE:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +create table if not exists customer +( + uuid uuid unique references RbacObject (uuid), + reference int not null unique check (reference between 10000 and 99999), + prefix character(3) unique, + adminUserName varchar(63) +); +--// diff --git a/src/main/resources/db/changelog/2022-07-28-051-hs-customer.sql b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql similarity index 57% rename from src/main/resources/db/changelog/2022-07-28-051-hs-customer.sql rename to src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql index 831fadcf..1e274a6e 100644 --- a/src/main/resources/db/changelog/2022-07-28-051-hs-customer.sql +++ b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql @@ -1,23 +1,23 @@ --- ======================================================== --- Customer example with RBAC --- -------------------------------------------------------- +--liquibase formatted sql -set session session authorization default; - -create table if not exists customer -( - uuid uuid unique references RbacObject (uuid), - reference int not null unique check (reference between 10000 and 99999), - prefix character(3) unique, - adminUserName varchar(63) -); +-- ============================================================================ +--changeset hs-customer-rbac-CREATE-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + Creates the related RbacObject through a BEFORE INSERT TRIGGER. + */ drop trigger if exists createRbacObjectForCustomer_Trigger on customer; create trigger createRbacObjectForCustomer_Trigger before insert on customer for each row execute procedure createRbacObject(); +--// + +-- ============================================================================ +--changeset hs-customer-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- create or replace function customerOwner(customer customer) returns RbacRoleDescriptor @@ -42,9 +42,18 @@ create or replace function customerTenant(customer customer) begin return roleDescriptor('customer', customer.uuid, 'tenant'); end; $$; +--// -create or replace function createRbacRulesForCustomer() +-- ============================================================================ +--changeset hs-customer-rbac-ROLES-CREATION:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles and their assignments for a new customer for the AFTER INSERT TRIGGER. + */ + +create or replace function createRbacRolesForCustomer() returns trigger language plpgsql strict as $$ @@ -83,12 +92,25 @@ begin return NEW; end; $$; -drop trigger if exists createRbacRulesForCustomer_Trigger on customer; -create trigger createRbacRulesForCustomer_Trigger +/* + An AFTER INSERT TRIGGER which creates the role structure for a new customer. + */ + +drop trigger if exists createRbacRolesForCustomer_Trigger on customer; +create trigger createRbacRolesForCustomer_Trigger after insert on customer for each row -execute procedure createRbacRulesForCustomer(); +execute procedure createRbacRolesForCustomer(); +--// + +-- ============================================================================ +--changeset hs-customer-rbac-ROLES-REMOVAL:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Deletes the roles and their assignments of a deleted customer for the BEFORE DELETE TRIGGER. + */ create or replace function deleteRbacRulesForCustomer() returns trigger @@ -98,27 +120,34 @@ declare objectTable varchar = 'customer'; begin if TG_OP = 'DELETE' then - - -- delete the owner role (for admininstrators) - call deleteRole(findRoleId(objectTable || '#' || NEW.prefix || '.owner')); - - -- delete the customer admin role - call deleteRole(findRoleId(objectTable || '#' || NEW.prefix || '.admin')); + call deleteRole(findRoleId(customerOwner(OLD))); + call deleteRole(findRoleId(customerAdmin(OLD))); + call deleteRole(findRoleId(customerTenant(OLD))); else raise exception 'invalid usage of TRIGGER BEFORE DELETE'; end if; end; $$; +/* + An BEFORE DELETE TRIGGER which deletes the role structure of a customer. + */ + drop trigger if exists deleteRbacRulesForCustomer_Trigger on customer; create trigger deleteRbacRulesForCustomer_Trigger before delete on customer for each row execute procedure deleteRbacRulesForCustomer(); +--// --- create a restricted view to access the textual customer ids a idName -set session session authorization default; --- ALTER TABLE customer ENABLE ROW LEVEL SECURITY; +-- ============================================================================ +--changeset hs-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates a view to the customer main table which maps the identifying name + (in this case, the prefix) to the objectUuid. + */ drop view if exists customer_iv; create or replace view customer_iv as select distinct target.uuid, target.prefix as idName @@ -126,58 +155,30 @@ select distinct target.uuid, target.prefix as idName -- TODO: Is it ok that everybody has access to this information? grant all privileges on customer_iv to restricted; +/* + Returns the objectUuid for a given identifying name (in this case the prefix). + */ create or replace function customerUuidByIdName(idName varchar) returns uuid language sql strict as $$ select uuid from customer_iv iv where iv.idName = customerUuidByIdName.idName; $$; +--// --- create RBAC restricted view + +-- ============================================================================ +--changeset hs-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + Creates a view to the customer main table with row-level limitatation + based on the 'view' permission of the current user or assumed roles. + */ set session session authorization default; --- ALTER TABLE customer ENABLE ROW LEVEL SECURITY; drop view if exists customer_rv; create or replace view customer_rv as select distinct target.* from customer as target where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'customer', currentSubjectIds())); grant all privileges on customer_rv to restricted; - - --- generate Customer test data - -set session session authorization default; -do language plpgsql $$ - declare - currentTask varchar; - custReference integer; - custRowId uuid; - custPrefix varchar; - custAdminName varchar; - begin - set hsadminng.currentUser to ''; - - for t in 0..9 - loop - currentTask = 'creating RBAC test customer #' || t; - set local hsadminng.currentUser to 'mike@hostsharing.net'; - set local hsadminng.assumedRoles = ''; - set local hsadminng.currentTask to currentTask; - - -- When a new customer is created, - custReference = 10000 + t; - custRowId = uuid_generate_v4(); - custPrefix = intToVarChar(t, 3); - custAdminName = 'admin@' || custPrefix || '.example.com'; - - raise notice 'creating customer %:%', custReference, custPrefix; - insert - into customer (reference, prefix, adminUserName) - values (custReference, custPrefix, custAdminName); - - commit; - - end loop; - - end; -$$; +--// diff --git a/src/main/resources/db/changelog/2022-07-29-062-hs-customer-test-data.sql b/src/main/resources/db/changelog/2022-07-29-062-hs-customer-test-data.sql new file mode 100644 index 00000000..358dd8e2 --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-29-062-hs-customer-test-data.sql @@ -0,0 +1,57 @@ +-- ============================================================================ +--changeset hs-customer-TEST-DATA-GENERATOR:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +create or replace procedure createCustomerTestData( + startCount integer, -- count of auto generated rows before the run + endCount integer, -- count of auto generated rows after the run + doCommitAfterEach boolean -- only for mass data creation outside of Liquibase +) + language plpgsql as $$ +declare + currentTask varchar; + custReference integer; + custRowId uuid; + custPrefix varchar; + custAdminName varchar; +begin + set hsadminng.currentUser to ''; + + for t in startCount..endCount + loop + currentTask = 'creating RBAC test customer #' || t; + set local hsadminng.currentUser to 'mike@hostsharing.net'; + set local hsadminng.assumedRoles = ''; + set local hsadminng.currentTask to currentTask; + + -- When a new customer is created, + custReference = 10000 + t; + custRowId = uuid_generate_v4(); + custPrefix = intToVarChar(t, 3); + custAdminName = 'admin@' || custPrefix || '.example.com'; + + raise notice 'creating customer %:%', custReference, custPrefix; + insert + into customer (reference, prefix, adminUserName) + values (custReference, custPrefix, custAdminName); + + if doCommitAfterEach then + commit; + end if; + + end loop; + +end; $$; +--// + + +-- ============================================================================ +--changeset hs-customer-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// +-- ---------------------------------------------------------------------------- + +do language plpgsql $$ + begin + call createCustomerTestData(0, 2, false); + end; +$$; +--// diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index ccb1b7d2..e7be8ebc 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -15,5 +15,11 @@ databaseChangeLog: file: db/changelog/2022-07-28-030-rbac-statistics.sql - include: file: db/changelog/2022-07-29-050-hs-base.sql + - include: + file: db/changelog/2022-07-29-060-hs-customer.sql + - include: + file: db/changelog/2022-07-29-061-hs-customer-rbac.sql + - include: + file: db/changelog/2022-07-29-062-hs-customer-test-data.sql From 61c50c46ed8553ff3d6780a4a10b335fd8df0e9d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 12:14:22 +0200 Subject: [PATCH 25/54] hs-customer SQL-Scripts in Liquibase --- .../2022-07-29-061-hs-customer-rbac.sql | 3 +-- .../2022-07-29-062-hs-customer-test-data.sql | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql index 1e274a6e..3c7d3bba 100644 --- a/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql +++ b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql @@ -104,6 +104,7 @@ create trigger createRbacRolesForCustomer_Trigger execute procedure createRbacRolesForCustomer(); --// + -- ============================================================================ --changeset hs-customer-rbac-ROLES-REMOVAL:1 endDelimiter:--// -- ---------------------------------------------------------------------------- @@ -116,8 +117,6 @@ create or replace function deleteRbacRulesForCustomer() returns trigger language plpgsql strict as $$ -declare - objectTable varchar = 'customer'; begin if TG_OP = 'DELETE' then call deleteRole(findRoleId(customerOwner(OLD))); diff --git a/src/main/resources/db/changelog/2022-07-29-062-hs-customer-test-data.sql b/src/main/resources/db/changelog/2022-07-29-062-hs-customer-test-data.sql index 358dd8e2..32789e91 100644 --- a/src/main/resources/db/changelog/2022-07-29-062-hs-customer-test-data.sql +++ b/src/main/resources/db/changelog/2022-07-29-062-hs-customer-test-data.sql @@ -1,7 +1,23 @@ +--liquibase formatted sql + + -- ============================================================================ --changeset hs-customer-TEST-DATA-GENERATOR:1 endDelimiter:--// -- ---------------------------------------------------------------------------- +/* + Generates a customer reference number for a given test data counter. + */ +create or replace function testCustomerReference(customerCount integer) + returns integer + returns null on null input + language plpgsql as $$ +begin + return 10000 + customerCount; +end; $$; +/* + Creates test data for the customer main table. + */ create or replace procedure createCustomerTestData( startCount integer, -- count of auto generated rows before the run endCount integer, -- count of auto generated rows after the run @@ -25,7 +41,7 @@ begin set local hsadminng.currentTask to currentTask; -- When a new customer is created, - custReference = 10000 + t; + custReference = testCustomerReference(t); custRowId = uuid_generate_v4(); custPrefix = intToVarChar(t, 3); custAdminName = 'admin@' || custPrefix || '.example.com'; From 2b630aadbca31aa989c9a7b10edb46fb28d24ced Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 12:37:40 +0200 Subject: [PATCH 26/54] hs-package SQL-Scripts in Liquibase and some bugfixes --- .../db/changelog/2022-07-28-005-rbac-base.sql | 45 +++--- .../2022-07-28-020-rbac-role-builder.sql | 8 ++ ...sql => 2022-07-29-070-hs-package-rbac.sql} | 134 +++++++++--------- .../2022-07-29-070-hs-package-test-data.sql | 62 ++++++++ .../changelog/2022-07-29-070-hs-package.sql | 13 ++ .../db/changelog/db.changelog-master.yaml | 6 + 6 files changed, 181 insertions(+), 87 deletions(-) rename src/main/resources/db/changelog/{22-hs-packages.sql => 2022-07-29-070-hs-package-rbac.sql} (54%) create mode 100644 src/main/resources/db/changelog/2022-07-29-070-hs-package-test-data.sql create mode 100644 src/main/resources/db/changelog/2022-07-29-070-hs-package.sql diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index 7006b0e3..df406c35 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -45,9 +45,12 @@ declare objectId uuid; begin insert - into RbacReference (type) values ('RbacUser') returning uuid into objectId; + into RbacReference (type) + values ('RbacUser') + returning uuid into objectId; insert - into RbacUser (uuid, name) values (objectid, userName); + into RbacUser (uuid, name) + values (objectid, userName); return objectId; end; $$; @@ -103,7 +106,9 @@ declare begin if TG_OP = 'INSERT' then insert - into RbacObject (objectTable) values (TG_TABLE_NAME) returning uuid into objectUuid; + into RbacObject (objectTable) + values (TG_TABLE_NAME) + returning uuid into objectUuid; NEW.uuid = objectUuid; return NEW; else @@ -153,9 +158,12 @@ declare referenceId uuid; begin insert - into RbacReference (type) values ('RbacRole') returning uuid into referenceId; + into RbacReference (type) + values ('RbacRole') + returning uuid into referenceId; insert - into RbacRole (uuid, objectUuid, roleType) values (referenceId, roleDescriptor.objectUuid, roleDescriptor.roleType); + into RbacRole (uuid, objectUuid, roleType) + values (referenceId, roleDescriptor.objectUuid, roleDescriptor.roleType); return referenceId; end; $$; @@ -254,9 +262,12 @@ begin if (refId is null) then raise notice 'createPermission: % %', forObjectUuid, permitOps[i]; insert - into RbacReference ("type") values ('RbacPermission') returning uuid into refId; + into RbacReference ("type") + values ('RbacPermission') + returning uuid into refId; insert - into RbacPermission (uuid, objectUuid, op) values (refId, forObjectUuid, permitOps[i]); + into RbacPermission (uuid, objectUuid, op) + values (refId, forObjectUuid, permitOps[i]); end if; raise notice 'addPermission: %', refId; permissionIds = permissionIds || refId; @@ -357,14 +368,16 @@ create or replace procedure grantPermissionsToRole(roleUuid uuid, permissionIds language plpgsql as $$ begin raise notice 'grantPermissionsToRole: % -> %', roleUuid, permissionIds; + if cardinality(permissionIds) = 0 then return; end if; + for i in array_lower(permissionIds, 1)..array_upper(permissionIds, 1) loop perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole'); perform assertReferenceType('permissionId (descendant)', permissionIds[i], 'RbacPermission'); - -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (roleId, permissionIds[i], true); -- assumeV1 insert - into RbacGrants (ascendantUuid, descendantUuid) values (roleUuid, permissionIds[i]); + into RbacGrants (ascendantUuid, descendantUuid, follow) + values (roleUuid, permissionIds[i], true); end loop; end; $$; @@ -379,7 +392,6 @@ begin raise exception 'Cyclic role grant detected between % and %', subRoleId, superRoleId; end if; - -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (superRoleId, subRoleId, doapply); -- assumeV1 insert into RbacGrants (ascendantUuid, descendantUuid, follow) values (superRoleId, subRoleId, doFollow) @@ -403,10 +415,9 @@ begin perform assertReferenceType('roleId (ascendant)', roleId, 'RbacRole'); perform assertReferenceType('userId (descendant)', userId, 'RbacUser'); - -- INSERT INTO RbacGrants (ascendantUuid, descendantUuid, apply) VALUES (userId, roleId, true); -- assumeV1 insert - into RbacGrants (ascendantUuid, descendantUuid) - values (userId, roleId) + into RbacGrants (ascendantUuid, descendantUuid, follow) + values (userId, roleId, true) on conflict do nothing; -- TODO: remove? end; $$; --// @@ -646,10 +657,10 @@ end; $$; --changeset rbac-base-pgsql-roles:1 endDelimiter:--// -- ------------------------------------------------------------------ -CREATE ROLE admin; -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin; +create role admin; +grant all privileges on all tables in schema public to admin; -CREATE ROLE restricted; -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; +create role restricted; +grant all privileges on all tables in schema public to restricted; --// diff --git a/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql index 2c71640b..35ec5ea8 100644 --- a/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql +++ b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql @@ -21,6 +21,14 @@ begin return row (createPermissions(forObjectUuid, permitOps))::RbacPermissions; end; $$; +create or replace function withoutPermissions() + returns RbacPermissions + language plpgsql + strict as $$ +begin + return row (array[]::uuid[]); +end; $$; + --// --changeset rbac-role-builder-super-roles:1 endDelimiter:--// diff --git a/src/main/resources/db/changelog/22-hs-packages.sql b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql similarity index 54% rename from src/main/resources/db/changelog/22-hs-packages.sql rename to src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql index 91d91cc9..2555ab87 100644 --- a/src/main/resources/db/changelog/22-hs-packages.sql +++ b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql @@ -1,22 +1,28 @@ --- ======================================================== --- Package example with RBAC --- -------------------------------------------------------- +--liquibase formatted sql -set session session authorization default; +-- ============================================================================ +--changeset hs-package-rbac-CREATE-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + Creates the related RbacObject through a BEFORE INSERT TRIGGER. + */ +drop trigger if exists createRbacObjectForPackage_Trigger on package; +create trigger createRbacObjectForPackage_Trigger + before insert + on package + for each row +execute procedure createRbacObject(); +--// -create table if not exists package -( - uuid uuid unique references RbacObject (uuid), - name character varying(5), - customerUuid uuid references customer (uuid) -); + +-- ============================================================================ +--changeset hs-package-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- create or replace function packageOwner(pac package) returns RbacRoleDescriptor returns null on null input language plpgsql as $$ -declare - roleDesc RbacRoleDescriptor; begin return roleDescriptor('package', pac.uuid, 'admin'); end; $$; @@ -36,16 +42,16 @@ create or replace function packageTenant(pac package) begin return roleDescriptor('package', pac.uuid, 'tenant'); end; $$; +--// -drop trigger if exists createRbacObjectForPackage_Trigger on package; -create trigger createRbacObjectForPackage_Trigger - before insert - on package - for each row -execute procedure createRbacObject(); - -create or replace function createRbacRulesForPackage() +-- ============================================================================ +--changeset hs-package-rbac-ROLES-CREATION:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + Creates the roles and their assignments for a new package for the AFTER INSERT TRIGGER. + */ +create or replace function createRbacRolesForPackage() returns trigger language plpgsql strict as $$ @@ -63,14 +69,14 @@ begin -- an owner role is created and assigned to the customer's admin role packageOwnerRoleUuid = createRole( packageOwner(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), + withoutPermissions(), beneathRole(customerAdmin(parentCustomer)) ); -- an owner role is created and assigned to the package owner role packageAdminRoleUuid = createRole( packageAdmin(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit', 'add-unixuser', 'add-domain']), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['add-unixuser', 'add-domain']), beneathRole(packageOwnerRoleUuid) ); @@ -85,12 +91,25 @@ begin return NEW; end; $$; -drop trigger if exists createRbacRulesForPackage_Trigger on package; -create trigger createRbacRulesForPackage_Trigger +/* + An AFTER INSERT TRIGGER which creates the role structure for a new package. + */ + +drop trigger if exists createRbacRolesForPackage_Trigger on package; +create trigger createRbacRolesForPackage_Trigger after insert on package for each row -execute procedure createRbacRulesForPackage(); +execute procedure createRbacRolesForPackage(); +--// + +-- ============================================================================ +--changeset hs-package-rbac-ROLES-REMOVAL:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Deletes the roles and their assignments of a deleted package for the BEFORE DELETE TRIGGER. + */ create or replace function deleteRbacRulesForPackage() returns trigger @@ -98,64 +117,39 @@ create or replace function deleteRbacRulesForPackage() strict as $$ begin if TG_OP = 'DELETE' then - -- TODO + call deleteRole(findRoleId(packageOwner(OLD))); + call deleteRole(findRoleId(packageAdmin(OLD))); + call deleteRole(findRoleId(packageTenant(OLD))); else raise exception 'invalid usage of TRIGGER BEFORE DELETE'; end if; end; $$; -drop trigger if exists deleteRbacRulesForPackage_Trigger on customer; +/* + An BEFORE DELETE TRIGGER which deletes the role structure of a package. + */ + +drop trigger if exists deleteRbacRulesForPackage_Trigger on package; create trigger deleteRbacRulesForPackage_Trigger before delete - on customer + on package for each row execute procedure deleteRbacRulesForPackage(); +--// --- create RBAC-restricted view -set session session authorization default; --- ALTER TABLE package ENABLE ROW LEVEL SECURITY; + +-- ============================================================================ +--changeset hs-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates a view to the customer main table which maps the identifying name + (in this case, the prefix) to the objectUuid. + */ drop view if exists package_rv; create or replace view package_rv as select distinct target.* from package as target where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'package', currentSubjectIds())); grant all privileges on package_rv to restricted; - - --- generate Package test data - -do language plpgsql $$ - declare - cust customer; - pacName varchar; - currentTask varchar; - custAdmin varchar; - begin - set hsadminng.currentUser to ''; - - for cust in (select * from customer) - loop - -- CONTINUE WHEN cust.reference < 18000; - - for t in 0..randominrange(1, 2) - loop - pacName = cust.prefix || to_char(t, 'fm00'); - currentTask = 'creating RBAC test package #' || pacName || ' for customer ' || cust.prefix || ' #' || - cust.uuid; - raise notice 'task: %', currentTask; - - custAdmin = 'admin@' || cust.prefix || '.example.com'; - set local hsadminng.currentUser to custAdmin; - set local hsadminng.assumedRoles = ''; - set local hsadminng.currentTask to currentTask; - - insert - into package (name, customerUuid) - values (pacName, cust.uuid); - - commit; - end loop; - end loop; - end; -$$; - +--// diff --git a/src/main/resources/db/changelog/2022-07-29-070-hs-package-test-data.sql b/src/main/resources/db/changelog/2022-07-29-070-hs-package-test-data.sql new file mode 100644 index 00000000..6e08b1e0 --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-29-070-hs-package-test-data.sql @@ -0,0 +1,62 @@ +--liquibase formatted sql + +-- ============================================================================ +--changeset hs-package-TEST-DATA-GENERATOR:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + Creates test data for the package main table. + */ +create or replace procedure createPackageTestData( + minCustomerReference integer, -- skip customers with reference below this + doCommitAfterEach boolean -- only for mass data creation outside of Liquibase +) + language plpgsql as $$ + declare + cust customer; + pacName varchar; + currentTask varchar; + custAdmin varchar; + begin + set hsadminng.currentUser to ''; + + for cust in (select * from customer) + loop + CONTINUE WHEN cust.reference < minCustomerReference; + + for t in 0..randominrange(1, 2) + loop + pacName = cust.prefix || to_char(t, 'fm00'); + currentTask = 'creating RBAC test package #' || pacName || ' for customer ' || cust.prefix || ' #' || + cust.uuid; + raise notice 'task: %', currentTask; + + custAdmin = 'admin@' || cust.prefix || '.example.com'; + set local hsadminng.currentUser to custAdmin; + set local hsadminng.assumedRoles = ''; + set local hsadminng.currentTask to currentTask; + + insert + into package (name, customerUuid) + values (pacName, cust.uuid); + end loop; + end loop; + + if doCommitAfterEach then + commit; + end if; + end; +$$; +--// + + +-- ============================================================================ +--changeset hs-package-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// +-- ---------------------------------------------------------------------------- + +do language plpgsql $$ + begin + call createPackageTestData(0, false); + end; +$$; +--// + diff --git a/src/main/resources/db/changelog/2022-07-29-070-hs-package.sql b/src/main/resources/db/changelog/2022-07-29-070-hs-package.sql new file mode 100644 index 00000000..95b925d5 --- /dev/null +++ b/src/main/resources/db/changelog/2022-07-29-070-hs-package.sql @@ -0,0 +1,13 @@ +--liquibase formatted sql + +-- ============================================================================ +--changeset hs-package-MAIN-TABLE:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +create table if not exists package +( + uuid uuid unique references RbacObject (uuid), + name character varying(5), + customerUuid uuid references customer (uuid) +); +--// diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index e7be8ebc..ea0e640f 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -21,5 +21,11 @@ databaseChangeLog: file: db/changelog/2022-07-29-061-hs-customer-rbac.sql - include: file: db/changelog/2022-07-29-062-hs-customer-test-data.sql + - include: + file: db/changelog/2022-07-29-070-hs-package.sql + - include: + file: db/changelog/2022-07-29-070-hs-package-rbac.sql + - include: + file: db/changelog/2022-07-29-070-hs-package-test-data.sql From 414149f0a15afc5a61c8684923184a82eb4b8190 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 14:24:50 +0200 Subject: [PATCH 27/54] introduce Context service --- build.gradle | 1 + .../config/PostgreSQL95CustomDialect.java | 13 +++++ .../hsadminng/context/Context.java | 51 +++++++++++++++++++ .../{ => controller}/TestController.java | 11 ++-- src/main/resources/application.yml | 21 +++++--- .../hsadminng/RbacIntegrationTests.java | 26 ---------- .../context/ContextIntegrationTests.java | 43 ++++++++++++++++ src/test/resources/application.yml | 10 ++-- 8 files changed, 132 insertions(+), 44 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java create mode 100644 src/main/java/net/hostsharing/hsadminng/context/Context.java rename src/main/java/net/hostsharing/hsadminng/{ => controller}/TestController.java (77%) delete mode 100644 src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java create mode 100644 src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java diff --git a/build.gradle b/build.gradle index 6f6c6dc2..79a36101 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.liquibase:liquibase-core' implementation 'org.springframework.data:spring-data-rest-hal-explorer' + implementation 'com.vladmihalcea:hibernate-types-55:2.17.1' compileOnly 'org.projectlombok:lombok' diff --git a/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java b/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java new file mode 100644 index 00000000..70781854 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java @@ -0,0 +1,13 @@ +package net.hostsharing.hsadminng.config; + +import com.vladmihalcea.hibernate.type.array.StringArrayType; +import org.hibernate.dialect.PostgreSQL95Dialect; + +@SuppressWarnings("unused") // configured in application.yml +public class PostgreSQL95CustomDialect extends PostgreSQL95Dialect { + + public PostgreSQL95CustomDialect() { + this.registerHibernateType(2003, StringArrayType.class.getName()); + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/context/Context.java b/src/main/java/net/hostsharing/hsadminng/context/Context.java new file mode 100644 index 00000000..82693667 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/context/Context.java @@ -0,0 +1,51 @@ +package net.hostsharing.hsadminng.context; + +import org.springframework.stereotype.Service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +@Service +public class Context { + + @PersistenceContext + private EntityManager em; + + @Transactional(Transactional.TxType.MANDATORY) + public void setCurrentUser(final String userName) { + em.createNativeQuery( + String.format( + "set local hsadminng.currentUser = '%s';", + userName + ) + ).executeUpdate(); + assumeNoSpecialRole(); + } + + public String getCurrentUser() { + return String.valueOf(em.createNativeQuery("select currentUser()").getSingleResult()); + } + + @Transactional(Transactional.TxType.MANDATORY) + public void assumeRoles(final String roles) { + em.createNativeQuery( + String.format( + "set local hsadminng.assumedRoles = '%s';", + roles + ) + ).executeUpdate(); + } + + @Transactional(Transactional.TxType.MANDATORY) + public void assumeNoSpecialRole() { + em.createNativeQuery( + "set local hsadminng.assumedRoles = '';" + ).executeUpdate(); + } + + public String[] getAssumedRoles() { + return (String[]) em.createNativeQuery("select assumedRoles()").getSingleResult(); + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/TestController.java b/src/main/java/net/hostsharing/hsadminng/controller/TestController.java similarity index 77% rename from src/main/java/net/hostsharing/hsadminng/TestController.java rename to src/main/java/net/hostsharing/hsadminng/controller/TestController.java index fbd7f6ce..483cf5f1 100644 --- a/src/main/java/net/hostsharing/hsadminng/TestController.java +++ b/src/main/java/net/hostsharing/hsadminng/controller/TestController.java @@ -1,5 +1,7 @@ -package net.hostsharing.hsadminng; +package net.hostsharing.hsadminng.controller; +import net.hostsharing.hsadminng.context.Context; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -15,6 +17,9 @@ public class TestController { @PersistenceContext private EntityManager em; + @Autowired + private Context context; + @ResponseBody @RequestMapping(value = "/api/ping", method = RequestMethod.GET) public String ping() { @@ -25,8 +30,8 @@ public class TestController { @ResponseBody @RequestMapping(value = "/api/currentUser", method = RequestMethod.GET) public String currentUser() { - em.createNativeQuery("SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';").executeUpdate(); - em.createNativeQuery("SET LOCAL hsadminng.assumedRoles = '';").executeUpdate(); + context.setCurrentUser("mike@hostsharing.net"); + final var query = em.createNativeQuery("select currentUser()"); return query.getSingleResult() + "\n"; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5cd25fa5..7bcbda1b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,14 +1,19 @@ spring: - datasource: - driver-class-name: org.postgresql.Driver - password: password - url: jdbc:postgresql://localhost:5432/postgres - username: postgres + datasource: + driver-class-name: org.postgresql.Driver + password: password + url: jdbc:postgresql://localhost:5432/postgres + username: postgres - sql: - init: - mode: never + sql: + init: + mode: never + + jpa: + properties: + hibernate: + dialect: net.hostsharing.hsadminng.config.PostgreSQL95CustomDialect liquibase: contexts: dev diff --git a/src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java b/src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java deleted file mode 100644 index a6f39d89..00000000 --- a/src/test/java/net/hostsharing/hsadminng/RbacIntegrationTests.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.hostsharing.hsadminng; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.transaction.Transactional; - -@DataJpaTest -class RbacIntegrationTests { - - @PersistenceContext - private EntityManager em; - - @Test - @Transactional - void currentUser() { - em.createNativeQuery("SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';").executeUpdate(); - em.createNativeQuery("SET LOCAL hsadminng.assumedRoles = '';").executeUpdate(); - - final var result = em.createNativeQuery("select currentUser()").getSingleResult(); - Assertions.assertThat(result).isEqualTo("mike@hostsharing.net"); - } -} diff --git a/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java b/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java new file mode 100644 index 00000000..25b90e4e --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java @@ -0,0 +1,43 @@ +package net.hostsharing.hsadminng.context; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.ComponentScan; + +import javax.transaction.Transactional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@ComponentScan(basePackageClasses = Context.class) +class ContextIntegrationTests { + + @Autowired + private Context context; + + @Test + @Transactional + void setCurrentUser() { + context.setCurrentUser("mike@hostsharing.net"); + + final var currentUser = context.getCurrentUser(); + assertThat(currentUser).isEqualTo("mike@hostsharing.net"); + + final var assumedRoles = context.getAssumedRoles(); + assertThat(assumedRoles).isEmpty(); + } + + @Test + @Transactional + void assumeRoles() { + context.setCurrentUser("mike@hostsharing.net"); + context.assumeRoles("customer#aaa.owner;customer#aab.owner"); + + final var currentUser = context.getCurrentUser(); + assertThat(currentUser).isEqualTo("mike@hostsharing.net"); + + final var assumedRoles = context.getAssumedRoles(); + assertThat(assumedRoles).containsExactlyInAnyOrder("customer#aaa.owner", "customer#aab.owner"); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index addd94d1..0629162b 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -12,7 +12,7 @@ spring: properties: hibernate: default_schema: public - dialect: org.hibernate.dialect.PostgreSQLDialect + dialect: net.hostsharing.hsadminng.config.PostgreSQL95CustomDialect hibernate: ddl-auto: none show-sql: true @@ -29,9 +29,5 @@ logging: level: liquibase: INFO - # spring.datasource.driver-class-name=org.postgresql.Driver -# spring.datasource.url=${DB_URL} -# spring.datasource.username=${DB_USERNAME} -# spring.datasource.password=${DB_PASSWORD} - -# spring.jpa.properties.hibernate.default-schema=public +liquibase: + contexts: dev,tc From 5126514877610bbe7e22e3a245803b295d90350f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 15:39:35 +0200 Subject: [PATCH 28/54] improved README.md with TOC --- README.md | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6c04e7d6..16391f5a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,16 @@ # hsadminNg Development + +- [Setting up the Development Environment](#setting-up-the-development-environment) + - [SDKMAN](#sdkman) + - [PostgreSQL Server](#postgresql-server) + - [Markdown with PlantUML plugin](#markdown-with-plantuml-plugin) + - [Other Tools](#other-tools) +- [Running the SQL files](#running-the-sql-files) + - [For RBAC](#for-rbac) + - [For Historization](#for-historization) + + ## Setting up the Development Environment All instructions assume that you're using a current Linux operating system. @@ -26,11 +37,21 @@ If you have at least Docker, the Java JDK and Gradle installed in appropriate ve pg-sql-run # downloads + runs PostgreSQL in a Docker container on localhost:5432 gw bootRun # compiles and runs the application on localhost:8080 - curl http://localhost:8080/api/ping # will reply with "pong" - curl http://localhost:8080/api/currentUser # will set+retrieve a current user + # the following command should reply with "pong": + curl http://localhost:8080/api/ping + + # the following command should return a JSON array with just the customer aac: + curl \ + -H 'current-user: mike@hostsharing.net' \ + -H 'assumed-roles: customer#aac.admin' \ + http://localhost:8080/api/customer The latter `curl` command actually goes through the database server. + +If you want a formatted JSON output, you can pipe the result to `jq` or similar too.s + + If you still need to install some of these tools, find some hints in the next chapters. @@ -121,9 +142,16 @@ Again, given the container is running, to restore the backup from ~/backup, run: pg-sql-restore <~/backup/hsadmin-ng-postgres.sql.gz -### Markdown with PlantUML plugin +### Markdown -Can you see the following diagram? +To generate the TOC (Table of Contents), a little bash script from a +[Blog Article](https://medium.com/@acrodriguez/one-liner-to-generate-a-markdown-toc-f5292112fd14) was used. + +To render the Markdown files, especially to watch embedded PlantUML diagrams, you can use one of the following methods: + +#### Render Markdown embedded PlantUML + +Can you see the following diagram right in your IDE? ```plantuml @startuml @@ -135,7 +163,7 @@ me -> you: Install some tooling! If not, you need to install some tooling. -#### for IntelliJ IDEA (or derived products) +##### for IntelliJ IDEA (or derived products) You just need the bundled Markdown plugin enabled and install and activate the PlantUML plugin in its settings: @@ -149,7 +177,7 @@ sudo apt install graphviz ``` -#### Ubuntu Linux command line +##### Ubuntu Linux command line ```sh sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive-extra-utils texlive-latex-extra pandoc-plantuml-filter @@ -159,10 +187,13 @@ sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive pandoc --filter pandoc-plantuml rbac.md -o rbac.pdf ``` -#### for other IDEs / operating systems +##### for other IDEs / operating systems If you have figured out how it works, please add instructions above this section. +### Other Tools + +**jq**: a JSON formatter, on Debian'oid systems you can install it with `sudo apt-get install jq` ## Running the SQL files From 46957dc5904527f18deb9fa0ec2e36e636bc67ff Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 15:53:20 +0200 Subject: [PATCH 29/54] bugfix in pureIdentifier + findUuidByIdName and CustomerEntity+Repository --- .../hostsharing/hsadminng/TestController.java | 16 ++++++++ .../hsadminng/controller/TestController.java | 38 ------------------- .../customer/CustomerController.java | 37 ++++++++++++++++++ .../hsadminng/customer/CustomerEntity.java | 16 ++++++++ .../customer/CustomerRepository.java | 9 +++++ .../db/changelog/2022-07-28-005-rbac-base.sql | 9 +++-- 6 files changed, 84 insertions(+), 41 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/TestController.java delete mode 100644 src/main/java/net/hostsharing/hsadminng/controller/TestController.java create mode 100644 src/main/java/net/hostsharing/hsadminng/customer/CustomerController.java create mode 100644 src/main/java/net/hostsharing/hsadminng/customer/CustomerEntity.java create mode 100644 src/main/java/net/hostsharing/hsadminng/customer/CustomerRepository.java diff --git a/src/main/java/net/hostsharing/hsadminng/TestController.java b/src/main/java/net/hostsharing/hsadminng/TestController.java new file mode 100644 index 00000000..c90dee65 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/TestController.java @@ -0,0 +1,16 @@ +package net.hostsharing.hsadminng; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class TestController { + + @ResponseBody + @RequestMapping(value = "/api/ping", method = RequestMethod.GET) + public String ping() { + return "pong\n"; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/controller/TestController.java b/src/main/java/net/hostsharing/hsadminng/controller/TestController.java deleted file mode 100644 index 483cf5f1..00000000 --- a/src/main/java/net/hostsharing/hsadminng/controller/TestController.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.hostsharing.hsadminng.controller; - -import net.hostsharing.hsadminng.context.Context; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.transaction.Transactional; - -@Controller -public class TestController { - - @PersistenceContext - private EntityManager em; - - @Autowired - private Context context; - - @ResponseBody - @RequestMapping(value = "/api/ping", method = RequestMethod.GET) - public String ping() { - return "pong\n"; - } - - @Transactional - @ResponseBody - @RequestMapping(value = "/api/currentUser", method = RequestMethod.GET) - public String currentUser() { - context.setCurrentUser("mike@hostsharing.net"); - - final var query = em.createNativeQuery("select currentUser()"); - return query.getSingleResult() + "\n"; - } -} diff --git a/src/main/java/net/hostsharing/hsadminng/customer/CustomerController.java b/src/main/java/net/hostsharing/hsadminng/customer/CustomerController.java new file mode 100644 index 00000000..d9ac35a4 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/customer/CustomerController.java @@ -0,0 +1,37 @@ +package net.hostsharing.hsadminng.customer; + +import net.hostsharing.hsadminng.context.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.transaction.Transactional; +import java.util.List; + +@Controller +public class CustomerController { + + @Autowired + private Context context; + + @Autowired + private CustomerRepository customerRepository; + + @ResponseBody + @RequestMapping(value = "/api/customer", method = RequestMethod.GET) + @Transactional + public List listCustomers( + @RequestHeader(value = "current-user") String userName, + @RequestHeader(value="assumed-roles", required=false) String assumedRoles + ) { + context.setCurrentUser(userName); + if ( assumedRoles != null && !assumedRoles.isBlank() ) { + context.assumeRoles(assumedRoles); + } + return customerRepository.findAll(); + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/customer/CustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/customer/CustomerEntity.java new file mode 100644 index 00000000..ad8d6d8f --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/customer/CustomerEntity.java @@ -0,0 +1,16 @@ +package net.hostsharing.hsadminng.customer; + +import lombok.Getter; + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Table(name = "customer_rv") +@Getter +public class CustomerEntity { + private @Id UUID uuid; + private String prefix; + private int reference; + private @Column(name="adminusername")String adminUserName; +} diff --git a/src/main/java/net/hostsharing/hsadminng/customer/CustomerRepository.java b/src/main/java/net/hostsharing/hsadminng/customer/CustomerRepository.java new file mode 100644 index 00000000..03ed76d5 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/customer/CustomerRepository.java @@ -0,0 +1,9 @@ +package net.hostsharing.hsadminng.customer; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface CustomerRepository extends JpaRepository { + +} diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index df406c35..1e5c77b3 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -583,7 +583,7 @@ begin end; $$; create or replace function pureIdentifier(rawIdentifier varchar) - returns uuid + returns varchar returns null on null input language plpgsql as $$ begin @@ -596,11 +596,14 @@ create or replace function findUuidByIdName(objectTable varchar, objectIdName va language plpgsql as $$ declare sql varchar; + uuid uuid; begin objectTable := pureIdentifier(objectTable); objectIdName := pureIdentifier(objectIdName); - sql := objectTable || 'UuidByIdName(' || objectIdName || ');'; - execute sql; + sql := format('select * from %sUuidByIdName(%L);', objectTable, objectIdName); + raise notice 'sql: %', sql; + execute sql into uuid; + return uuid; end; $$; create or replace function currentSubjectIds() From 4721d1be23d4e2d832c424d7ed5b7f882c1b7094 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 16:13:38 +0200 Subject: [PATCH 30/54] PackagerEntity, -Repository and -Controller --- README.md | 23 ++++++++---- .../CustomerController.java | 2 +- .../CustomerEntity.java | 2 +- .../CustomerRepository.java | 2 +- .../hspackage/PackageController.java | 37 +++++++++++++++++++ .../hsadminng/hspackage/PackageEntity.java | 21 +++++++++++ .../hspackage/PackageRepository.java | 9 +++++ 7 files changed, 85 insertions(+), 11 deletions(-) rename src/main/java/net/hostsharing/hsadminng/{customer => hscustomer}/CustomerController.java (96%) rename src/main/java/net/hostsharing/hsadminng/{customer => hscustomer}/CustomerEntity.java (86%) rename src/main/java/net/hostsharing/hsadminng/{customer => hscustomer}/CustomerRepository.java (78%) create mode 100644 src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java create mode 100644 src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java create mode 100644 src/main/java/net/hostsharing/hsadminng/hspackage/PackageRepository.java diff --git a/README.md b/README.md index 16391f5a..d5c819cb 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,15 @@ - [Setting up the Development Environment](#setting-up-the-development-environment) - - [SDKMAN](#sdkman) - - [PostgreSQL Server](#postgresql-server) - - [Markdown with PlantUML plugin](#markdown-with-plantuml-plugin) - - [Other Tools](#other-tools) + - [SDKMAN](#sdkman) + - [PostgreSQL Server](#postgresql-server) + - [Markdown](#markdown) + - [Render Markdown embedded PlantUML](#render-markdown-embedded-plantuml) + - [Other Tools](#other-tools) - [Running the SQL files](#running-the-sql-files) - - [For RBAC](#for-rbac) - - [For Historization](#for-historization) + - [For RBAC](#for-rbac) + - [For Historization](#for-historization) + ## Setting up the Development Environment @@ -40,12 +42,17 @@ If you have at least Docker, the Java JDK and Gradle installed in appropriate ve # the following command should reply with "pong": curl http://localhost:8080/api/ping - # the following command should return a JSON array with just the customer aac: + # the following command should return a JSON array with just all customers: curl \ -H 'current-user: mike@hostsharing.net' \ - -H 'assumed-roles: customer#aac.admin' \ http://localhost:8080/api/customer + # the following command should return a JSON array with just all packages visible for the admin of the customer aab: + curl \ + -H 'current-user: mike@hostsharing.net' \ + -H 'assumed-roles: customer#aab.admin' \ + http://localhost:8080/api/package + The latter `curl` command actually goes through the database server. diff --git a/src/main/java/net/hostsharing/hsadminng/customer/CustomerController.java b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java similarity index 96% rename from src/main/java/net/hostsharing/hsadminng/customer/CustomerController.java rename to src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java index d9ac35a4..b172a068 100644 --- a/src/main/java/net/hostsharing/hsadminng/customer/CustomerController.java +++ b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.customer; +package net.hostsharing.hsadminng.hscustomer; import net.hostsharing.hsadminng.context.Context; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/net/hostsharing/hsadminng/customer/CustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java similarity index 86% rename from src/main/java/net/hostsharing/hsadminng/customer/CustomerEntity.java rename to src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java index ad8d6d8f..05f0b6a3 100644 --- a/src/main/java/net/hostsharing/hsadminng/customer/CustomerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.customer; +package net.hostsharing.hsadminng.hscustomer; import lombok.Getter; diff --git a/src/main/java/net/hostsharing/hsadminng/customer/CustomerRepository.java b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java similarity index 78% rename from src/main/java/net/hostsharing/hsadminng/customer/CustomerRepository.java rename to src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java index 03ed76d5..293cbef8 100644 --- a/src/main/java/net/hostsharing/hsadminng/customer/CustomerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.customer; +package net.hostsharing.hsadminng.hscustomer; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java new file mode 100644 index 00000000..1441c67f --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java @@ -0,0 +1,37 @@ +package net.hostsharing.hsadminng.hspackage; + +import net.hostsharing.hsadminng.context.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.transaction.Transactional; +import java.util.List; + +@Controller +public class PackageController { + + @Autowired + private Context context; + + @Autowired + private PackageRepository packageRepository; + + @ResponseBody + @RequestMapping(value = "/api/package", method = RequestMethod.GET) + @Transactional + public List listPackages( + @RequestHeader(value = "current-user") String userName, + @RequestHeader(value = "assumed-roles", required = false) String assumedRoles + ) { + context.setCurrentUser(userName); + if (assumedRoles != null && !assumedRoles.isBlank()) { + context.assumeRoles(assumedRoles); + } + return packageRepository.findAll(); + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java new file mode 100644 index 00000000..16feb7eb --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java @@ -0,0 +1,21 @@ +package net.hostsharing.hsadminng.hspackage; + +import lombok.Getter; +import net.hostsharing.hsadminng.hscustomer.CustomerEntity; + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Table(name = "package_rv") +@Getter +public class PackageEntity { + + private @Id UUID uuid; + + private String name; + + @ManyToOne(optional = false) + @JoinColumn(name = "customeruuid") + private CustomerEntity customer; +} diff --git a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageRepository.java b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageRepository.java new file mode 100644 index 00000000..0f3c8c38 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageRepository.java @@ -0,0 +1,9 @@ +package net.hostsharing.hsadminng.hspackage; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface PackageRepository extends JpaRepository { + +} From b20920d6469093d3de3ecc685672c727d529b527 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 29 Jul 2022 16:25:46 +0200 Subject: [PATCH 31/54] make package owner/admin/tenant roles assumable --- .../2022-07-29-070-hs-package-rbac.sql | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql index 2555ab87..8dc717f9 100644 --- a/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql +++ b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql @@ -139,7 +139,34 @@ execute procedure deleteRbacRulesForPackage(); -- ============================================================================ ---changeset hs-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--// +--changeset hs-package-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates a view to the package main table which maps the identifying name + (in this case, actually the column `name`) to the objectUuid. + */ +drop view if exists package_iv; +create or replace view package_iv as +select distinct target.uuid, target.name as idName + from package as target; +-- TODO: Is it ok that everybody has access to this information? +grant all privileges on package_iv to restricted; + +/* + Returns the objectUuid for a given identifying name (in this case, actually the column `name`). + */ +create or replace function packageUuidByIdName(idName varchar) + returns uuid + language sql + strict as $$ +select uuid from package_iv iv where iv.idName = packageUuidByIdName.idName; +$$; +--// + + +-- ============================================================================ +--changeset hs-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- /* From 5b8688cd635cfb80c0cb0ef10d84e4ecf00d7cfe Mon Sep 17 00:00:00 2001 From: hsh-michaelhoennig Date: Sat, 30 Jul 2022 14:41:11 +0200 Subject: [PATCH 32/54] amended README.md for MacOS --- .gitignore | 1 + README.md | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index c2065bc2..7c865940 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ out/ ### VS Code ### .vscode/ +/gradle/wrapper/ diff --git a/README.md b/README.md index d5c819cb..bb1e4587 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # hsadminNg Development +This documents gives an overview of the development environment and tools. +For architecture consider the files in the `doc` and `adr` folder. + - [Setting up the Development Environment](#setting-up-the-development-environment) - [SDKMAN](#sdkman) @@ -10,17 +13,16 @@ - [Running the SQL files](#running-the-sql-files) - [For RBAC](#for-rbac) - [For Historization](#for-historization) - ## Setting up the Development Environment -All instructions assume that you're using a current Linux operating system. -Everything is tested on Ubuntu Linux 22.04. +All instructions assume that you're using a current _Linux_ or _MacOS_ operating system. +Everything is tested on _Ubuntu Linux 22.04_ and _MacOS Monterey (12.4)_. To be able to build and run the Java Spring Boot application, you need the following tools: -- Docker 20.x +- Docker 20.x (on MacOS you also need *Docker Desktop* or similar) - PostgreSQL Server 13.7-bullseye (see instructions below to install and run in Docker) - Java JDK 17.x - Gradle in some not too outdated version (7.4 will be installed via wrapper) @@ -32,7 +34,7 @@ If you have at least Docker, the Java JDK and Gradle installed in appropriate ve cd your-hsadmin-ng-directory gradle wrapper # downloads Gradle 7.5 into the project - source .alias # creates some comforable bash aliases, e.g. 'gw'='./gradlew' + source .aliases # creates some comforable bash aliases, e.g. 'gw'='./gradlew' gw test # compiles and runs unit- and integration-tests @@ -56,8 +58,7 @@ If you have at least Docker, the Java JDK and Gradle installed in appropriate ve The latter `curl` command actually goes through the database server. -If you want a formatted JSON output, you can pipe the result to `jq` or similar too.s - +If you want a formatted JSON output, you can pipe the result to `jq` or similar. If you still need to install some of these tools, find some hints in the next chapters. @@ -65,6 +66,7 @@ If you still need to install some of these tools, find some hints in the next ch ### SDKMAN *SdkMan* is not necessary, but helpful to install and switch between different versions of SDKs (Software-Development-Kits) and development tools in general, e.g. *JDK* and *Gradle*. +It is available for _Linux_ and _MacOS_, _WSL_, _Cygwin_, _Solaris_ and _FreeBSD_. You can get it from: https://sdkman.io/. @@ -159,6 +161,7 @@ To render the Markdown files, especially to watch embedded PlantUML diagrams, yo #### Render Markdown embedded PlantUML Can you see the following diagram right in your IDE? +I mean a real graphic diagram, not just some markup code. ```plantuml @startuml @@ -200,7 +203,9 @@ If you have figured out how it works, please add instructions above this section ### Other Tools -**jq**: a JSON formatter, on Debian'oid systems you can install it with `sudo apt-get install jq` +**jq**: a JSON formatter. +On _Debian_'oid systems you can install it with `sudo apt-get install jq`. +On _MacOS_ you can install it with `brew install jq`, given you have _brew_ installed. ## Running the SQL files From 2610ecd3112eaf6133474b500e22bdd91502e4ef Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 30 Jul 2022 15:25:44 +0200 Subject: [PATCH 33/54] add PackageControllerTest as WebMvcTest through the REST API --- .../hsadminng/hscustomer/CustomerEntity.java | 4 ++ .../hsadminng/hspackage/PackageEntity.java | 4 ++ .../hspackage/PackageControllerTest.java | 62 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerTest.java diff --git a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java index 05f0b6a3..8a5f99b3 100644 --- a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.hscustomer; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import javax.persistence.*; import java.util.UUID; @@ -8,6 +10,8 @@ import java.util.UUID; @Entity @Table(name = "customer_rv") @Getter +@NoArgsConstructor +@AllArgsConstructor public class CustomerEntity { private @Id UUID uuid; private String prefix; diff --git a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java index 16feb7eb..0792e843 100644 --- a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.hspackage; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import net.hostsharing.hsadminng.hscustomer.CustomerEntity; import javax.persistence.*; @@ -9,6 +11,8 @@ import java.util.UUID; @Entity @Table(name = "package_rv") @Getter +@NoArgsConstructor +@AllArgsConstructor public class PackageEntity { private @Id UUID uuid; diff --git a/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerTest.java b/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerTest.java new file mode 100644 index 00000000..aae210ce --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerTest.java @@ -0,0 +1,62 @@ +package net.hostsharing.hsadminng.hspackage; + +import net.hostsharing.hsadminng.context.Context; +import net.hostsharing.hsadminng.hscustomer.CustomerEntity; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.util.UUID; + +import static java.util.Arrays.asList; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(PackageController.class) +class PackageControllerTest { + + final CustomerEntity cust = new CustomerEntity(UUID.randomUUID(), "xyz", 10001, "xyz@example.com"); + final PackageEntity pac00 = new PackageEntity(UUID.randomUUID(), "xyz00", cust); + final PackageEntity pac01 = new PackageEntity(UUID.randomUUID(), "xyz01", cust); + final PackageEntity pac02 = new PackageEntity(UUID.randomUUID(), "xyz02", cust); + @Autowired + MockMvc mockMvc; + @MockBean + private Context contextMock; + @MockBean + private PackageRepository packageRepositoryMock; + + @Test + void findAll() throws Exception { + + // given + final var givenPacs = asList(pac00, pac01, pac02); + when(packageRepositoryMock.findAll()).thenReturn(givenPacs); + + // when + final var pacs = mockMvc.perform(MockMvcRequestBuilders + .get("/api/package") + .header("current-user", "mike@hostsharing.net") + .header("assumed-roles", "customer#xyz.admin") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(3))) + .andExpect(jsonPath("$[0].name", is("xyz00"))) + .andExpect(jsonPath("$[1].uuid", is(pac01.getUuid().toString()))) + .andExpect(jsonPath("$[2].customer.prefix", is("xyz"))); + + verify(contextMock).setCurrentUser("mike@hostsharing.net"); + verify(contextMock).assumeRoles("customer#xyz.admin"); + } + +} From fe4fef2752d2100da40dc5509a13909e3571d38a Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 30 Jul 2022 18:17:45 +0200 Subject: [PATCH 34/54] add CustomerControllerRestTest and -UnitTest --- README.md | 4 +- .../hscustomer/CustomerController.java | 2 +- .../CustomerControllerRestTest.java | 49 +++++++++++++++++ .../CustomerControllerUnitTest.java | 55 +++++++++++++++++++ .../hsadminng/hscustomer/TestCustomer.java | 14 +++++ 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerUnitTest.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java diff --git a/README.md b/README.md index bb1e4587..61b93490 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,13 @@ If you have at least Docker, the Java JDK and Gradle installed in appropriate ve # the following command should return a JSON array with just all customers: curl \ -H 'current-user: mike@hostsharing.net' \ - http://localhost:8080/api/customer + http://localhost:8080/api/customers # the following command should return a JSON array with just all packages visible for the admin of the customer aab: curl \ -H 'current-user: mike@hostsharing.net' \ -H 'assumed-roles: customer#aab.admin' \ - http://localhost:8080/api/package + http://localhost:8080/api/packages The latter `curl` command actually goes through the database server. diff --git a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java index b172a068..ffed4130 100644 --- a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java +++ b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java @@ -21,7 +21,7 @@ public class CustomerController { private CustomerRepository customerRepository; @ResponseBody - @RequestMapping(value = "/api/customer", method = RequestMethod.GET) + @RequestMapping(value = "/api/customers", method = RequestMethod.GET) @Transactional public List listCustomers( @RequestHeader(value = "current-user") String userName, diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java new file mode 100644 index 00000000..267a1c4a --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java @@ -0,0 +1,49 @@ +package net.hostsharing.hsadminng.hscustomer; + +import net.hostsharing.hsadminng.context.Context; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static java.util.UUID.randomUUID; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(CustomerController.class) +class CustomerControllerRestTest { + + @Autowired + MockMvc mockMvc; + @MockBean + Context contextMock; + @MockBean + CustomerRepository customerRepositoryMock; + + @Test + void apiCustomersWillReturnCustomersFromRepository() throws Exception { + + // given + when(customerRepositoryMock.findAll()).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); + + // when + final var pacs = mockMvc.perform(MockMvcRequestBuilders + .get("/api/customers") + .header("current-user", "mike@hostsharing.net") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix()))) + .andExpect(jsonPath("$[1].reference", is(TestCustomer.yyy.getReference()))); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerUnitTest.java new file mode 100644 index 00000000..71bd0dab --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerUnitTest.java @@ -0,0 +1,55 @@ +package net.hostsharing.hsadminng.hscustomer; + +import net.hostsharing.hsadminng.context.Context; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class CustomerControllerUnitTest { + + @Mock + Context contextMock; + @Mock + CustomerRepository customerRepositoryMock; + + @InjectMocks + CustomerController customerController; + + @Test + void apiCustomersWillReturnCustomersFromRepository() throws Exception { + + // given + when(customerRepositoryMock.findAll()).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); + + // when + final var pacs = customerController.listCustomers("mike@hostsharing.net", null); + + // then + assertThat(pacs).hasSize(2); + verify(contextMock).setCurrentUser("mike@hostsharing.net"); + verify(contextMock, never()).assumeRoles(any()); + } + + @Test + void findAllWithAssumedCustomerAdminRole() throws Exception { + + // given + when(customerRepositoryMock.findAll()).thenReturn(singletonList(TestCustomer.yyy)); + + // when + final var pacs = customerController.listCustomers("mike@hostsharing.net", "customer#yyy.admin"); + + // then + assertThat(pacs).hasSize(1); + verify(contextMock).setCurrentUser("mike@hostsharing.net"); + verify(contextMock).assumeRoles("customer#yyy.admin"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java b/src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java new file mode 100644 index 00000000..ab887848 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java @@ -0,0 +1,14 @@ +package net.hostsharing.hsadminng.hscustomer; + +import static java.util.UUID.randomUUID; + +public class TestCustomer { + + static final CustomerEntity xxx = customer("xxx", 10001, "xxx@example.com"); + static final CustomerEntity yyy = customer("yyy", 10002, "yyy@example.com"); + + + static public CustomerEntity customer(final String prefix, final int reference, final String adminName) { + return new CustomerEntity(randomUUID(), prefix, reference, adminName); + } +} From 08804bd45cf8ef5e9411c8778946cde6a3d00687 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 30 Jul 2022 18:23:02 +0200 Subject: [PATCH 35/54] use plural for /api/packages too --- .../hsadminng/hspackage/PackageController.java | 2 +- ...ControllerTest.java => PackageControllerRestTest.java} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/test/java/net/hostsharing/hsadminng/hspackage/{PackageControllerTest.java => PackageControllerRestTest.java} (93%) diff --git a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java index 1441c67f..19f0b48c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java +++ b/src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java @@ -21,7 +21,7 @@ public class PackageController { private PackageRepository packageRepository; @ResponseBody - @RequestMapping(value = "/api/package", method = RequestMethod.GET) + @RequestMapping(value = "/api/packages", method = RequestMethod.GET) @Transactional public List listPackages( @RequestHeader(value = "current-user") String userName, diff --git a/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerTest.java b/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java similarity index 93% rename from src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerTest.java rename to src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java index aae210ce..bb5957be 100644 --- a/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java @@ -21,7 +21,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(PackageController.class) -class PackageControllerTest { +class PackageControllerRestTest { final CustomerEntity cust = new CustomerEntity(UUID.randomUUID(), "xyz", 10001, "xyz@example.com"); final PackageEntity pac00 = new PackageEntity(UUID.randomUUID(), "xyz00", cust); @@ -30,9 +30,9 @@ class PackageControllerTest { @Autowired MockMvc mockMvc; @MockBean - private Context contextMock; + Context contextMock; @MockBean - private PackageRepository packageRepositoryMock; + PackageRepository packageRepositoryMock; @Test void findAll() throws Exception { @@ -43,7 +43,7 @@ class PackageControllerTest { // when final var pacs = mockMvc.perform(MockMvcRequestBuilders - .get("/api/package") + .get("/api/packages") .header("current-user", "mike@hostsharing.net") .header("assumed-roles", "customer#xyz.admin") .accept(MediaType.APPLICATION_JSON)) From 1463807b8933bdd9a0b0b0d6f5f0e1054060f74d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 30 Jul 2022 18:23:18 +0200 Subject: [PATCH 36/54] initial test-concept documentation --- doc/test-concept.md | 109 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 doc/test-concept.md diff --git a/doc/test-concept.md b/doc/test-concept.md new file mode 100644 index 00000000..237102a6 --- /dev/null +++ b/doc/test-concept.md @@ -0,0 +1,109 @@ +## Test-Concept + + +- [Unit-Tests](#unit-tests) +- [REST-Tests](#rest-tests) +- [Integration-Tests](#integration-tests) +- [Acceptance-Tests](#acceptance-tests) +- [Performance-Tests](#performance-tests) +- [System-Integration-Tests](#system-integration-tests) + + +### General Issues + +The following test concept uses the terms "double" and "mock" (maybe in inflected form like "mocking" or "mocked"), "whitebox-test" and "blackbox-tests" which I would like to define first. + +#### Test-Doubles, Dummies, Fakes, Mocks, Spies and Stubs + +A "double" is a general term for something which replaces a real implementation of a dependency of the unit under test. +This can be a "dummy", a "fake", a "mock", a "spy" or a "stub". +Often the term "mock" is used in a generic way, because typical mocking libraries like *Mockito* can also be used as dummies or spies and can replace fakes. + +A fake would be a double without using any library, but rather a manual fake implementation of a dependency. +Where our APIs should be designed in a way that it's possible, using a mocking library like *Mockito* often leads to shorter test code. + +#### Whitebox- and Blackbox-Tests + +A whitebox-test knows and considers the internals of an implementation, e.g. it knows which dependencies it needs and can test special, implementation-dependent cases. + +A blackbox-test does not know and not consider such internals of an implementation, it just tests externally observable behaviour. + +### Kinds of Tests + +Depending on the concrete aspects which we want to test, we are using different kinds of tests as described as follows. + +#### Unit-Tests + +In this project a *Unit* for *UnitTests* can be a single method (function), a class or even a group of classes which express a common concept. +The unit are whitebox-tests and count into test-code-coverage. + +Unit-Test in this project are implemented with *JUnit Jupiter*, *Mockito* and *AssertJ*. + +Unit-Tests do not use any external systems, not even a database. +They just test the unit, not any dependencies or proper integration with dependencies. + +Such tests usually run very fast and should test all branches. + +These Tests are always named `...UnitTest` and can automatically run in the build-process. + + +#### REST-Tests + +At the level of REST-Controllers, *Spring's* `WebMvcTest`, a special kind of Unit-Test, are utilized. +Such tests issue REST-requests through a mocked REST-Layer and therefore use the controllers similar to a real client. +Otherwise, the implementation technologies are like those of Unit-Tests. + +Being unit-tests, also REST-tests are whitebox-tests and count into test-code-coverage. + +Like other Unit-Tests, REST-Test do not use any external systems, not even a database. +They just test the REST-related parts of the unit, e.g. URL-Mappings, HTTP-Headers and proper JSON encoding of request and response data. +Other dependencies and integrations with such are not tested on this level. + +Such tests usually run very fast, but should focus on REST-specific issues, leaving branch-testing to pure Unit-Tests. + +These Tests are always named `...RestTest` and can automatically run in the build-process. + + +#### Integration-Tests + +Integration-Tests in this context mean integration with support systems like databases or messaging-systems, but not integration with external systems. + +Integration-tests, are blackbox-tests and do not count into test-code-coverage. + +Such tests are implemented with *JUnit Jupiter* through some sort of `@SpringBootTest`, e.g. `DataJpaTest` and usually utilize *Testcontainers* and *Docker* to wrap the supporting system, e.g. the *PostgreSQL* database. +*Mockito* can also be used for this kind of tests, to separate multiple integrations. + +Integration-Tests are relatively slow and therefore should focus on the integration. +Internal issues should be tested through Unit-Tests. + +These Tests are always named `...IntegrationTest` and can automatically run in the build-process. + + +#### Acceptance-Tests + +We define Acceptance-Tests as test which describe user-stories, respectively high-level business requirements. +Acceptance-Tests run on a fully integrated and deployed system with deployed doubles for external systems. + +Acceptance-tests, are blackbox-tests and do not count into test-code-coverage. + +TODO: Complete the Acceptance-Tests test concept. + + +#### Performance-Tests + +Performance-critical scenarios have to be identified and a special performance-test has to be implemented. + +The implementation-technologie depends on the scenario. + +Performance-tests, are blackbox-tests and do not count into test-code-coverage. + +Such tests usually are very slow and should not be automatically run in the build-pipeline but manually, after critical areas have been changed. + + +#### System-Integration-Tests + +We define System-Integration-Tests as test in which this system is deployed in a production-like environment to test integration with external systems. + +System-Integration-tests, are blackbox-tests and do not count into test-code-coverage. + +TODO: Complete the System-Integration-Tests test concept. From 53d3d680218e936a6b8b404a68fff04cc94e1f22 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 30 Jul 2022 18:37:32 +0200 Subject: [PATCH 37/54] introduce TestPackage.java --- .../hsadminng/hscustomer/TestCustomer.java | 6 +++--- .../hspackage/PackageControllerRestTest.java | 18 +++++++----------- .../hsadminng/hspackage/TestPackage.java | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hspackage/TestPackage.java diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java b/src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java index ab887848..f90b7e42 100644 --- a/src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java +++ b/src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java @@ -4,11 +4,11 @@ import static java.util.UUID.randomUUID; public class TestCustomer { - static final CustomerEntity xxx = customer("xxx", 10001, "xxx@example.com"); - static final CustomerEntity yyy = customer("yyy", 10002, "yyy@example.com"); + public static final CustomerEntity xxx = hsCustomer("xxx", 10001, "xxx@example.com"); + static final CustomerEntity yyy = hsCustomer("yyy", 10002, "yyy@example.com"); - static public CustomerEntity customer(final String prefix, final int reference, final String adminName) { + static public CustomerEntity hsCustomer(final String prefix, final int reference, final String adminName) { return new CustomerEntity(randomUUID(), prefix, reference, adminName); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java index bb5957be..1100080c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hspackage; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.hscustomer.CustomerEntity; +import net.hostsharing.hsadminng.hscustomer.TestCustomer; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -23,10 +23,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebMvcTest(PackageController.class) class PackageControllerRestTest { - final CustomerEntity cust = new CustomerEntity(UUID.randomUUID(), "xyz", 10001, "xyz@example.com"); - final PackageEntity pac00 = new PackageEntity(UUID.randomUUID(), "xyz00", cust); - final PackageEntity pac01 = new PackageEntity(UUID.randomUUID(), "xyz01", cust); - final PackageEntity pac02 = new PackageEntity(UUID.randomUUID(), "xyz02", cust); @Autowired MockMvc mockMvc; @MockBean @@ -38,25 +34,25 @@ class PackageControllerRestTest { void findAll() throws Exception { // given - final var givenPacs = asList(pac00, pac01, pac02); + final var givenPacs = asList(TestPackage.xxx00, TestPackage.xxx01, TestPackage.xxx02); when(packageRepositoryMock.findAll()).thenReturn(givenPacs); // when final var pacs = mockMvc.perform(MockMvcRequestBuilders .get("/api/packages") .header("current-user", "mike@hostsharing.net") - .header("assumed-roles", "customer#xyz.admin") + .header("assumed-roles", "customer#xxx.admin") .accept(MediaType.APPLICATION_JSON)) // then .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(3))) - .andExpect(jsonPath("$[0].name", is("xyz00"))) - .andExpect(jsonPath("$[1].uuid", is(pac01.getUuid().toString()))) - .andExpect(jsonPath("$[2].customer.prefix", is("xyz"))); + .andExpect(jsonPath("$[0].name", is("xxx00"))) + .andExpect(jsonPath("$[1].uuid", is(TestPackage.xxx01.getUuid().toString()))) + .andExpect(jsonPath("$[2].customer.prefix", is("xxx"))); verify(contextMock).setCurrentUser("mike@hostsharing.net"); - verify(contextMock).assumeRoles("customer#xyz.admin"); + verify(contextMock).assumeRoles("customer#xxx.admin"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hspackage/TestPackage.java b/src/test/java/net/hostsharing/hsadminng/hspackage/TestPackage.java new file mode 100644 index 00000000..37651767 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hspackage/TestPackage.java @@ -0,0 +1,17 @@ +package net.hostsharing.hsadminng.hspackage; + +import net.hostsharing.hsadminng.hscustomer.CustomerEntity; +import net.hostsharing.hsadminng.hscustomer.TestCustomer; + +import static java.util.UUID.randomUUID; + +public class TestPackage { + + public static final PackageEntity xxx00 = hsPackage(TestCustomer.xxx, "xxx00"); + public static final PackageEntity xxx01 = hsPackage(TestCustomer.xxx, "xxx01"); + public static final PackageEntity xxx02 = hsPackage(TestCustomer.xxx, "xxx02"); + + public static PackageEntity hsPackage(final CustomerEntity customer, final String name) { + return new PackageEntity(randomUUID(), name, customer); + } +} From 5d4fb853830776fe3ac143eda54ca4a7c3712a86 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sun, 31 Jul 2022 15:16:49 +0200 Subject: [PATCH 38/54] TODO tracking --- Glossary.md | 18 -- TODO-progress.png | Bin 0 -> 7433 bytes TODO.md | 82 +++++++++ ...2022-07-18.row-level-security-mechanism.md | 160 ------------------ doc/test-concept.md | 19 +-- tools/todo-progress | 22 +++ tools/todo-progress.gnuplot | 23 +++ 7 files changed, 132 insertions(+), 192 deletions(-) delete mode 100644 Glossary.md create mode 100644 TODO-progress.png create mode 100644 TODO.md delete mode 100644 adr/2022-07-18.row-level-security-mechanism.md create mode 100755 tools/todo-progress create mode 100644 tools/todo-progress.gnuplot diff --git a/Glossary.md b/Glossary.md deleted file mode 100644 index 2757ecc0..00000000 --- a/Glossary.md +++ /dev/null @@ -1,18 +0,0 @@ -# hsadminNg Glossary - -### Business Object - -Represents an object from the - -### Tenant - -The RBAC - -### RBAC - -abbreviation for *Role Based Access Control* - -### Role Based Access Control (RBAC) - -A system to control access to business objects by defining users, roles, and permissions. -For more information see diff --git a/TODO-progress.png b/TODO-progress.png new file mode 100644 index 0000000000000000000000000000000000000000..b08f6f1258fbe868fc6355ba7820cb87c81f4642 GIT binary patch literal 7433 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 +| Datum | Budget | Aufwand | Leistung | Restschuld | +|------------|-------:|--------:|---------:|-----------:| +| 2022-07-17 | 553 | 44 | 0 | 553 | +| 2022-07-24 | 553 | 8 | 0 | 553 | +| 2022-07-31 | 553 | 143 | 76 | 477 | + + + diff --git a/adr/2022-07-18.row-level-security-mechanism.md b/adr/2022-07-18.row-level-security-mechanism.md deleted file mode 100644 index 51b72245..00000000 --- a/adr/2022-07-18.row-level-security-mechanism.md +++ /dev/null @@ -1,160 +0,0 @@ -# Use VIEWs with JOIN into Permission-Assignments for Row-Level-Security - -**Status:** -- [x] proposed by Michael Hönnig -- [ ] accepted by (Participants) -- [ ] rejected by (Participants) -- [ ] superseded by (superseding ADR) - -## Context and Problem Statement - -We need to decide how to apply the access rules defined in our RBAC system to the visibility of table rows for the accessing user. - -The core problem here is, that in our RBAC system, determining the permissions of the accessing user has to consider a hierarchy of roles. - -### Technical Background - -The session variable `hsadminng.currentUser` contains the accessing (domain-level) user, which is unrelated to the PostgreSQL user). - -Given is a stored function `isPermissionGrantedToSubject` which detects if the accessing user has a given permission (e.g. 'view'). - -Given is also a stored function `queryAllPermissionsOfSubjectId` which returns the flattened view to all permissions assigned to the given accessing user. - -In the following code snippets `customer` is just an example domain table. - -## Considered Options - -* Perform Visibility-Checks programmatically in the Backend -* Add Visibility-Checks in the Backend -* POLICY with ENABLE ROW LEVEL SECURITY -* VIEW-RULE with ON SELECT DO INSTEAD -* VIEW with JOIN into Flattened Permissions - -### Perform Visibility-Checks programmatically in the Backend - -In this solution, the database ignores row level visibility and returns all rows which match a given query. Afterwards, the result is filtered programmatically with Java-code in the backend. - -#### Advantages - -Very flexible access, programmatic, rules could be implemented. - -The role-hierarchy and permissions for currently logged-in users user could be cached in the backend. - -The access logic can be tested in pure Java unit tests. - -At least regarding this aspect, an in-memory database could be used for integration testing; though the recursive Role-evaluation uses PostgreSQL features anyway. - -#### Disadvantages - -It's inefficient when initial query is not very restrictive, e.g. as on overview pages in a frontend, which often show all accessible objects, large parts or even whole database tables need to be transferred from the database to the backend. - -It's error-prone and security leaks can happen too easily, because after every query the access rights for all participating joins have to be considered. - -### Add Visibility-Checks in the Backend - -In this solution again, the database ignores row level visibility and returns all rows which match a given query. And the backend adds filter conditions to each query sent to the database. - -#### Advantages - -At least regarding this aspect, an in-memory database could be used for integration testing. - -#### Disadvantages - -It's error-prone and security leaks can happen too easily, because for every query the access rights for all participating joins have to be considered. - -### POLICY with ENABLE ROW LEVEL SECURITY - -For restricted DB-users, which are used by the backend, access to rows is filtered using a policy: - - SET SESSION AUTHORIZATION DEFAULT; - CREATE ROLE restricted; - GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; - ALTER TABLE customer ENABLE ROW LEVEL SECURITY; - CREATE POLICY customer_policy ON customer - FOR SELECT - TO restricted - USING ( - isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserId()) - ); - - SET SESSION AUTHORIZATION restricted; - SET hsadminng.currentUser TO 'alex@example.com'; - SELECT * from customer; -- will only return visible rows - -#### Advantages - -Using POLICY together with ENABLE ROW LEVEL SECURITY is the PostgreSQL native mechanism to control access to data on the role level. Therefore, it looked like an obvious and elegant solution. - -Every access at from the backend is under access control at the database level. - -### Disadvantages - -Unfortunately security mechanisms in PostgreSQL prevent the query optimizer to work well beyond ownership barriers (session user vs. table owner) and a SELECT from a table with 1 million objects needed over 30 seconds with our hierarchical RBAC policy. - -We are bound to PostgreSQL, including integration tests and testing the RBAC system itself. - -### VIEW-RULE with ON SELECT DO INSTEAD - - SET SESSION SESSION AUTHORIZATION DEFAULT; - CREATE VIEW cust_view AS - SELECT * FROM customer; - CREATE OR REPLACE RULE "_RETURN" AS - ON SELECT TO cust_view - DO INSTEAD - SELECT * FROM customer WHERE isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserId()); - - SET SESSION AUTHORIZATION restricted; - SET hsadminng.currentUser TO 'alex@example.com'; - SELECT * from customer; -- will only return visible rows - -#### Advantages - -Every access at from the backend is under access control at the database level. - -Also using ON UPDATE etc., original tables could be completely hidden from the backend, and thus improved security. - -### Disadvantages - -Unfortunately security mechanisms in PostgreSQL prevent the query optimizer to work well beyond ownership barriers (session user vs. table owner) and a SELECT from a table with 1 million objects needed over 30 seconds with our hierarchical RBAC policy. - -We are bound to PostgreSQL, including integration tests and testing the RBAC system itself. - -An extra view needed for every table. - - -### VIEW with JOIN into flattened permissions - -We do not access the tables directly from the backend, but via views which join the flattened permissions - - SET SESSION SESSION AUTHORIZATION DEFAULT; - CREATE OR REPLACE VIEW cust_view AS - SELECT c.id, c.reference, c.prefix - FROM customer AS c - JOIN queryAllPermissionsOfSubjectId(currentUserId()) AS p - ON p.tableName='customer' AND p.rowId=c.id AND p.op='view'; - GRANT ALL PRIVILEGES ON cust_view TO restricted; - - SET SESSION SESSION AUTHORIZATION restricted; - SET hsadminng.currentUser TO 'alex@example.com'; - SELECT * from cust_view; -- will only return visible rows - -Alternatively the JOIN could also be applied in a "ON SELECT DO INSTEAD"-RULE, if there is any advantage for later features. - -#### Advantages - -Every access at from the backend is under access control at the database level. - -No special PostgreSQL features needed; though the recursive Role-evaluation uses PostgreSQL features anyway. - -Very fast, on my laptop a SELECT * FROM a table with 1 million rows just took about 50ms. - -Also using ON UPDATE etc., original tables could be completely hidden from the backend, and thus improved security. - -### Disadvantages - -An extra view needed for every table. - - -## Decision Outcome - -We chose the option **"VIEW with JOIN into flattened permissions"** because it supports the best combination of performance and security with almost no disadvantge. diff --git a/doc/test-concept.md b/doc/test-concept.md index 237102a6..95feb30f 100644 --- a/doc/test-concept.md +++ b/doc/test-concept.md @@ -11,22 +11,11 @@ ### General Issues -The following test concept uses the terms "double" and "mock" (maybe in inflected form like "mocking" or "mocked"), "whitebox-test" and "blackbox-tests" which I would like to define first. +The following test concept uses terms like "double" and "mock" (maybe in inflected form like "mocking" or "mocked"), "whitebox-test" and "blackbox-tests" and "test-fixture". +Please look up their definition in the [glossary](glossary.md) -#### Test-Doubles, Dummies, Fakes, Mocks, Spies and Stubs - -A "double" is a general term for something which replaces a real implementation of a dependency of the unit under test. -This can be a "dummy", a "fake", a "mock", a "spy" or a "stub". -Often the term "mock" is used in a generic way, because typical mocking libraries like *Mockito* can also be used as dummies or spies and can replace fakes. - -A fake would be a double without using any library, but rather a manual fake implementation of a dependency. Where our APIs should be designed in a way that it's possible, using a mocking library like *Mockito* often leads to shorter test code. -#### Whitebox- and Blackbox-Tests - -A whitebox-test knows and considers the internals of an implementation, e.g. it knows which dependencies it needs and can test special, implementation-dependent cases. - -A blackbox-test does not know and not consider such internals of an implementation, it just tests externally observable behaviour. ### Kinds of Tests @@ -35,7 +24,9 @@ Depending on the concrete aspects which we want to test, we are using different #### Unit-Tests In this project a *Unit* for *UnitTests* can be a single method (function), a class or even a group of classes which express a common concept. -The unit are whitebox-tests and count into test-code-coverage. + +The unit are technically whitebox-tests and count into test-code-coverage. +But the whitebox-knowledge should only be used for the text-fixture. Unit-Test in this project are implemented with *JUnit Jupiter*, *Mockito* and *AssertJ*. diff --git a/tools/todo-progress b/tools/todo-progress new file mode 100755 index 00000000..8e01ad7b --- /dev/null +++ b/tools/todo-progress @@ -0,0 +1,22 @@ +#!/bin/bash +declare -a required=(gnuplot sponge) +for cmd in "${required[@]}"; do + command -v $cmd >/dev/null 2>&1 || { echo >&2 "Required '$cmd' not installed => aborting."; exit 1; } +done + +let budget=`grep '^| ... |' ' >.todo-progress.md +sed -e '1,/todo-progress begin:/d' -e '/todo-progress end./,$d' TODO.md >>.todo-progress.md +echo "| $(date --iso-8601) | $(printf "%6d" $budget) | $(printf "%7d" $effort) | $(printf "%8d" $output) | $(printf "%10d" $remainder) |" >>.todo-progress.md +echo '' >>.todo-progress.md +uniq <.todo-progress.md | sponge .todo-progress.md +sed -i -e '/todo-progress begin:/,/todo-progress end./!b' -e '/todo-progress end./!d;r .todo-progress.md' -e 'd' TODO.md + +sed -e's/^|//' <.todo-progress.md | tr '|' ';' | grep -v '|---' >.todo-progress.csv +gnuplot tools/todo-progress.gnuplot +rm .todo-progress.md .todo-progress.csv + diff --git a/tools/todo-progress.gnuplot b/tools/todo-progress.gnuplot new file mode 100644 index 00000000..69601fc0 --- /dev/null +++ b/tools/todo-progress.gnuplot @@ -0,0 +1,23 @@ +set xdata time # x-axis values are time (date) values +set timefmt "%Y-%m-%d" # date value format +set datafile separator ";" # CSV column separator is semicolon +set key autotitle columnhead # first data line contains column titles +set format x "%y-%m-%d" # display date format + +set xrange ["2022-07-11":"2022-10-31"] # x-axis value-range +set yrange [0:600] # y-axis value-range + +set key inside # graph legend style +set xtics rotate by -45 # rotate dates on x-axis 45deg for cleaner display +set title 'hsadmin-ng Projektfortschritt' # graph title + +set terminal png # output format +set term png size 920, 640 # output canvas size +set output 'TODO-progress.png' # output file name + +plot '.todo-progress.csv' using 1:2 with linespoints linetype rgb "black" linewidth 2, \ + '' using 1:3 with linespoints linetype rgb "red" linewidth 2, \ + '' using 1:4 with linespoints linetype rgb "green" linewidth 2, \ + '' using 1:5 with linespoints linetype rgb "blue" linewidth 2 + + From 27c5699c368fa2fe95ada45109d2123fc401ff0d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sun, 31 Jul 2022 18:56:03 +0200 Subject: [PATCH 39/54] add CustomerRepositoryIntegrationTest, fix testcontainers liquibase context and improve error messages --- .../config/PostgreSQL95CustomDialect.java | 1 + .../hscustomer/CustomerRepository.java | 3 + .../db/changelog/2022-07-28-005-rbac-base.sql | 16 +- .../db/changelog/2022-07-29-050-hs-base.sql | 55 +++++-- .../CustomerRepositoryIntegrationTest.java | 155 ++++++++++++++++++ src/test/resources/application.yml | 8 +- 6 files changed, 214 insertions(+), 24 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java diff --git a/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java b/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java index 70781854..5b86aba9 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java +++ b/src/main/java/net/hostsharing/hsadminng/config/PostgreSQL95CustomDialect.java @@ -8,6 +8,7 @@ public class PostgreSQL95CustomDialect extends PostgreSQL95Dialect { public PostgreSQL95CustomDialect() { this.registerHibernateType(2003, StringArrayType.class.getName()); + this.registerHibernateType(1111, "pg-uuid"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java index 293cbef8..dca4dfb8 100644 --- a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java @@ -1,9 +1,12 @@ package net.hostsharing.hsadminng.hscustomer; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import java.util.List; import java.util.UUID; public interface CustomerRepository extends JpaRepository { + List findByPrefixLike(final String prefix); } diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index 1e5c77b3..8255ca2b 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -601,8 +601,12 @@ begin objectTable := pureIdentifier(objectTable); objectIdName := pureIdentifier(objectIdName); sql := format('select * from %sUuidByIdName(%L);', objectTable, objectIdName); - raise notice 'sql: %', sql; - execute sql into uuid; + begin + raise notice 'sql: %', sql; + execute sql into uuid; + exception when OTHERS then + raise exception 'function %UuidByIdName(...) not found, add identity view support for table %', objectTable, objectTable; + end; return uuid; end; $$; @@ -622,8 +626,12 @@ declare roleUuidToAssume uuid; begin currentUserId := currentUserId(); + if currentUserId is null then + raise exception 'user % does not exist', currentUser(); + end if; + roleNames := assumedRoles(); - if (cardinality(roleNames) = 0) then + if cardinality(roleNames) = 0 then return array [currentUserId]; end if; @@ -645,7 +653,7 @@ begin and r.roleType = roleTypeToAssume into roleUuidToAssume; if (not isGranted(currentUserId, roleUuidToAssume)) then - raise exception 'user % has no permission to assume role %', currentUser(), roleUuidToAssume; + raise exception 'user % (%) has no permission to assume role % (%)', currentUser(), currentUserId, roleName, roleUuidToAssume; end if; roleIdsToAssume := roleIdsToAssume || roleUuidToAssume; end loop; diff --git a/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql b/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql index 043a995d..d1b42878 100644 --- a/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql +++ b/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql @@ -9,19 +9,44 @@ Otherwise these columns needed to be nullable and many queries would be more complicated. */ -create table Hostsharing +create table Global ( - uuid uuid primary key references RbacObject (uuid) + uuid uuid primary key references RbacObject (uuid), + name varchar(63) ); -create unique index Hostsharing_Singleton on Hostsharing ((0)); +create unique index Global_Singleton on Global ((0)); /** A single row to be referenced as a global object. */ insert - into RbacObject (objecttable) values ('hostsharing'); + into RbacObject (objecttable) values ('global'); insert - into Hostsharing (uuid) values ((select uuid from RbacObject where objectTable = 'hostsharing')); + into Global (uuid, name) values ((select uuid from RbacObject where objectTable = 'global'), 'hostsharing'); +--// + +-- ============================================================================ +--changeset hs-base-GLOBAL-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates a view to the global object table which maps the identifying name to the objectUuid. + */ +drop view if exists global_iv; +create or replace view global_iv as +select distinct target.uuid, target.name as idName + from global as target; +grant all privileges on global_iv to restricted; + +/* + Returns the objectUuid for a given identifying name (in this case the prefix). + */ +create or replace function globalUuidByIdName(idName varchar) + returns uuid + language sql + strict as $$ +select uuid from global_iv iv where iv.idName = globalUuidByIdName.idName; +$$; --// -- ============================================================================ @@ -35,12 +60,12 @@ create or replace function hostsharingAdmin() returns null on null input stable leakproof language sql as $$ - select 'global', (select uuid from RbacObject where objectTable = 'hostsharing'), 'admin'::RbacRoleType; +select 'global', (select uuid from RbacObject where objectTable = 'global'), 'admin'::RbacRoleType; $$; select createRole(hostsharingAdmin()); -- ============================================================================ ---changeset hs-base-ADMIN-USERS:1 context:dev endDelimiter:--// +--changeset hs-base-ADMIN-USERS:1 context:dev,test,tc endDelimiter:--// -- ---------------------------------------------------------------------------- /* Create two users and assign both to the administrators role. @@ -58,7 +83,7 @@ $$; -- ============================================================================ ---changeset hs-base-hostsharing-TEST:1 context:dev runAlways:true endDelimiter:--// +--changeset hs-base-hostsharing-TEST:1 context:dev,test,tc runAlways:true endDelimiter:--// -- ---------------------------------------------------------------------------- /* @@ -69,16 +94,16 @@ do language plpgsql $$ declare userName varchar; begin - set local hsadminng.currentUser = 'mike@hostsharing.net'; - select userName from RbacUser where uuid = currentUserId() into userName; - if userName <> 'mike@hostsharing.net' then - raise exception 'fetching initial currentUser failed'; - end if; - set local hsadminng.currentUser = 'sven@hostsharing.net'; select userName from RbacUser where uuid = currentUserId() into userName; if userName <> 'sven@hostsharing.net' then - raise exception 'fetching changed currentUser failed'; + raise exception 'setting or fetching initial currentUser failed, got: %', userName; + end if; + + set local hsadminng.currentUser = 'mike@hostsharing.net'; + select userName from RbacUser where uuid = currentUserId() into userName; + if userName = 'mike@hostsharing.net' then + raise exception 'currentUser should not change in one transaction, but did change, got: %', userName; end if; end; $$; --// diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java new file mode 100644 index 00000000..370b2ad0 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java @@ -0,0 +1,155 @@ +package net.hostsharing.hsadminng.hscustomer; + +import net.hostsharing.hsadminng.context.Context; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.orm.jpa.JpaSystemException; + +import javax.persistence.EntityManager; +import javax.transaction.Transactional; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@DataJpaTest +@ComponentScan(basePackageClasses = { Context.class, CustomerRepository.class }) +class CustomerRepositoryIntegrationTest { + + final static String adminUser = "mike@hostsharing.net"; + final static String customerAaa = "admin@aaa.example.com"; + + @Autowired + Context context; + + @Autowired + CustomerRepository customerRepository; + + @Autowired EntityManager em; + + @Test + @Transactional + void hostsharingAdminWithoutAssumedRoleCanViewAllCustomers() { + // given + context.setCurrentUser(adminUser); + + // when + final var actual = customerRepository.findAll(); + + // then + + assertThat(actual).hasSize(3) + .extracting(CustomerEntity::getPrefix) + .containsExactlyInAnyOrder("aaa", "aab", "aac"); + } + + @Test + @Transactional + void hostsharingAdminWithAssumedHostsharingAdminRoleCanViewAllCustomers() { + // given + context.setCurrentUser(adminUser); + context.assumeRoles("global#hostsharing.admin"); + + // when + final var actual = customerRepository.findAll(); + + // then + + assertThat(actual).hasSize(3) + .extracting(CustomerEntity::getPrefix) + .containsExactlyInAnyOrder("aaa", "aab", "aac"); + } + + @Test + @Transactional + void customerAdminWithoutAssumedRoleCanViewItsOwnCustomer() { + // given + context.setCurrentUser(customerAaa); + + // when + final var actual = customerRepository.findAll(); + + // then + + assertThat(actual).hasSize(1) + .extracting(CustomerEntity::getPrefix) + .containsExactly("aaa"); + } + + @Test + @Transactional + void customerAdminWithAssumedOwnedPackageAdminRoleCanViewItsOwnCustomer() { + // given + context.setCurrentUser(customerAaa); + context.assumeRoles("package#aaa00.admin"); + + // when + final var actual = customerRepository.findAll(); + + // then + assertThat(actual).hasSize(1) + .extracting(CustomerEntity::getPrefix) + .containsExactly("aaa"); + } + + @Test + @Transactional + void customerAdminWithAssumedAlienPackageAdminRoleCanViewItsOwnCustomer() { + // given + context.setCurrentUser(customerAaa); + context.assumeRoles("package#aab00.admin"); + + // when + final JpaSystemException thrown = + assertThrows(JpaSystemException.class, () -> customerRepository.findAll()); + + // then + assertThat(firstRootCauseMessageLineOf(thrown)).matches( + ".* user admin@aaa.example.com .* has no permission to assume role package#aab00#admin .*" + ); + } + + @Test + @Transactional + void unknownUserWithoutAssumedRoleCannotViewAnyCustomers() { + // given + context.setCurrentUser("unknown@example.org"); + + // when + final JpaSystemException thrown = + assertThrows(JpaSystemException.class, () -> customerRepository.findAll()); + + // then + assertThat(firstRootCauseMessageLineOf(thrown)).matches( + ".* user unknown@example.org does not exist.*" + ); + } + + @Test + @Transactional + void unknownUserWithAssumedRoleCannotViewAnyCustomers() { + // given + context.setCurrentUser("unknown@example.org"); + assertThat(context.getCurrentUser()).isEqualTo("unknown@example.org"); + context.assumeRoles("customer#aaa.admin"); + + + // when + final JpaSystemException thrown = + assertThrows(JpaSystemException.class, () -> customerRepository.findAll()); + + // then + assertThat(firstRootCauseMessageLineOf(thrown)).matches( + ".* user unknown@example.org does not exist.*" + ); + } + + private String firstRootCauseMessageLineOf(final JpaSystemException throwable) { + return Optional.ofNullable(throwable.getRootCause()) + .map(Throwable::getMessage) + .map( message -> message.split("\\r|\\n|\\r\\n", 0)[0]) + .orElse(null); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 0629162b..694e2d74 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -4,7 +4,8 @@ spring: platform: postgres datasource: - url: jdbc:tc:postgresql:12.9-alpine:///spring_boot_testcontainers + url: jdbc:tc:postgresql:13.7-bullseye:///spring_boot_testcontainers + url-local: jdbc:postgresql://localhost:5432/postgres username: postgres password: password @@ -23,11 +24,8 @@ spring: liquibase: change-log: classpath:/db/changelog/db.changelog-master.yaml - contexts: test + contexts: tc,test,dev logging: level: liquibase: INFO - -liquibase: - contexts: dev,tc From f58a68d1cc2d0e8b83cef7ebf5b217dbd59f631d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 1 Aug 2022 12:53:09 +0200 Subject: [PATCH 40/54] experimental CustomerRepositoryIntegrationTest with Given/When/Then classes --- .../CustomerRepositoryIntegrationTest.java | 267 ++++++++++-------- 1 file changed, 147 insertions(+), 120 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java index 370b2ad0..642405dd 100644 --- a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java @@ -1,26 +1,26 @@ package net.hostsharing.hsadminng.hscustomer; import net.hostsharing.hsadminng.context.Context; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.ComponentScan; +import org.springframework.core.NestedRuntimeException; import org.springframework.orm.jpa.JpaSystemException; import javax.persistence.EntityManager; import javax.transaction.Transactional; +import java.util.List; import java.util.Optional; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; @DataJpaTest @ComponentScan(basePackageClasses = { Context.class, CustomerRepository.class }) class CustomerRepositoryIntegrationTest { - final static String adminUser = "mike@hostsharing.net"; - final static String customerAaa = "admin@aaa.example.com"; - @Autowired Context context; @@ -29,127 +29,154 @@ class CustomerRepositoryIntegrationTest { @Autowired EntityManager em; - @Test - @Transactional - void hostsharingAdminWithoutAssumedRoleCanViewAllCustomers() { - // given - context.setCurrentUser(adminUser); + @Nested + class FindAll { - // when - final var actual = customerRepository.findAll(); + private final Given given = new Given(); + private When> when; + private final Then then = new Then(); - // then + @Test + public void hostsharingAdminWithoutAssumedRoleCanViewAllCustomers() { + given.currentUser("mike@hostsharing.net"); - assertThat(actual).hasSize(3) - .extracting(CustomerEntity::getPrefix) - .containsExactlyInAnyOrder("aaa", "aab", "aac"); + when(() -> customerRepository.findAll()); + + then.exactlyTheseCustomersAreReturned("aaa", "aab", "aac"); + } + + @Test + public void hostsharingAdminWithAssumedHostsharingAdminRoleCanViewAllCustomers() { + given.currentUser("mike@hostsharing.net"). + and().assumedRoles("global#hostsharing.admin"); + + when(() -> customerRepository.findAll()); + + then.exactlyTheseCustomersAreReturned("aaa", "aab", "aac"); + } + + @Test + public void customerAdminWithoutAssumedRoleCanViewOnlyItsOwnCustomer() { + given.currentUser("admin@aaa.example.com"); + + when(() -> customerRepository.findAll()); + + then.exactlyTheseCustomersAreReturned("aaa"); + } + + @Test + public void customerAdminWithAssumedOwnedPackageAdminRoleCanViewOnlyItsOwnCustomer() { + given.currentUser("admin@aaa.example.com"). + and().assumedRoles("package#aaa00.admin"); + + when(() -> customerRepository.findAll()); + + then.exactlyTheseCustomersAreReturned("aaa"); + } + + @Test + public void customerAdmin_withAssumedAlienPackageAdminRole_cannotViewAnyCustomer() { + given.currentUser("admin@aaa.example.com"). + and().assumedRoles("package#aab00.admin"); + + when(() -> customerRepository.findAll()); + + then.expectJpaSystemExceptionHasBeenThrown(). + and() + .expectRootCauseMessageMatches( + ".* user admin@aaa.example.com .* has no permission to assume role package#aab00#admin .*"); + } + + @Test + void unknownUser_withoutAssumedRole_cannotViewAnyCustomers() { + given.currentUser("unknown@example.org"); + + when(() -> customerRepository.findAll()); + + then.expectJpaSystemExceptionHasBeenThrown(). + and().expectRootCauseMessageMatches(".* user unknown@example.org does not exist.*"); + } + + @Test + @Transactional + void unknownUserWithAssumedCustomerRoleCannotViewAnyCustomers() { + given.currentUser("unknown@example.org"). + and().assumedRoles("customer#aaa.admin"); + + when(() -> customerRepository.findAll()); + + then.expectJpaSystemExceptionHasBeenThrown(). + and().expectRootCauseMessageMatches(".* user unknown@example.org does not exist.*"); + } + + void when(final Supplier> code) { + try { + when = new When<>(code.get()); + } catch (final NestedRuntimeException exc) { + when = new When<>(exc); + } + } + + private class Then { + + Then and() { + return this; + } + + void exactlyTheseCustomersAreReturned(final String... customerPrefixes) { + assertThat(when.actualResult) + .hasSize(customerPrefixes.length) + .extracting(CustomerEntity::getPrefix) + .containsExactlyInAnyOrder(customerPrefixes); + } + + Then expectJpaSystemExceptionHasBeenThrown() { + assertThat(when.actualException).isInstanceOf(JpaSystemException.class); + return this; + } + + void expectRootCauseMessageMatches(final String expectedMessage) { + assertThat(firstRootCauseMessageLineOf(when.actualException)).matches(expectedMessage); + } + } } - @Test - @Transactional - void hostsharingAdminWithAssumedHostsharingAdminRoleCanViewAllCustomers() { - // given - context.setCurrentUser(adminUser); - context.assumeRoles("global#hostsharing.admin"); - - // when - final var actual = customerRepository.findAll(); - - // then - - assertThat(actual).hasSize(3) - .extracting(CustomerEntity::getPrefix) - .containsExactlyInAnyOrder("aaa", "aab", "aac"); - } - - @Test - @Transactional - void customerAdminWithoutAssumedRoleCanViewItsOwnCustomer() { - // given - context.setCurrentUser(customerAaa); - - // when - final var actual = customerRepository.findAll(); - - // then - - assertThat(actual).hasSize(1) - .extracting(CustomerEntity::getPrefix) - .containsExactly("aaa"); - } - - @Test - @Transactional - void customerAdminWithAssumedOwnedPackageAdminRoleCanViewItsOwnCustomer() { - // given - context.setCurrentUser(customerAaa); - context.assumeRoles("package#aaa00.admin"); - - // when - final var actual = customerRepository.findAll(); - - // then - assertThat(actual).hasSize(1) - .extracting(CustomerEntity::getPrefix) - .containsExactly("aaa"); - } - - @Test - @Transactional - void customerAdminWithAssumedAlienPackageAdminRoleCanViewItsOwnCustomer() { - // given - context.setCurrentUser(customerAaa); - context.assumeRoles("package#aab00.admin"); - - // when - final JpaSystemException thrown = - assertThrows(JpaSystemException.class, () -> customerRepository.findAll()); - - // then - assertThat(firstRootCauseMessageLineOf(thrown)).matches( - ".* user admin@aaa.example.com .* has no permission to assume role package#aab00#admin .*" - ); - } - - @Test - @Transactional - void unknownUserWithoutAssumedRoleCannotViewAnyCustomers() { - // given - context.setCurrentUser("unknown@example.org"); - - // when - final JpaSystemException thrown = - assertThrows(JpaSystemException.class, () -> customerRepository.findAll()); - - // then - assertThat(firstRootCauseMessageLineOf(thrown)).matches( - ".* user unknown@example.org does not exist.*" - ); - } - - @Test - @Transactional - void unknownUserWithAssumedRoleCannotViewAnyCustomers() { - // given - context.setCurrentUser("unknown@example.org"); - assertThat(context.getCurrentUser()).isEqualTo("unknown@example.org"); - context.assumeRoles("customer#aaa.admin"); - - - // when - final JpaSystemException thrown = - assertThrows(JpaSystemException.class, () -> customerRepository.findAll()); - - // then - assertThat(firstRootCauseMessageLineOf(thrown)).matches( - ".* user unknown@example.org does not exist.*" - ); - } - - private String firstRootCauseMessageLineOf(final JpaSystemException throwable) { - return Optional.ofNullable(throwable.getRootCause()) + private String firstRootCauseMessageLineOf(final NestedRuntimeException exception) { + return Optional.ofNullable(exception.getRootCause()) .map(Throwable::getMessage) - .map( message -> message.split("\\r|\\n|\\r\\n", 0)[0]) - .orElse(null); + .map(message -> message.split("\\r|\\n|\\r\\n", 0)[0]) + .orElse(null); + } + + private class Given { + + Given and() { + return this; + } + + Given currentUser(final String currentUser) { + context.setCurrentUser(currentUser); + assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser); + return this; + } + + void assumedRoles(final String assumedRoles) { + context.assumeRoles(assumedRoles); + assertThat(context.getAssumedRoles()).as("precondition").containsExactly(assumedRoles.split(";")); + } + } + + private static class When { + + T actualResult; + NestedRuntimeException actualException; + + When(final T actualResult) { + this.actualResult = actualResult; + } + + When(final NestedRuntimeException exception) { + this.actualException = exception; + } } } From 03ee2cfd621e4a5b410f853c7dc02b7eb5ddc6c8 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 2 Aug 2022 11:51:36 +0200 Subject: [PATCH 41/54] add-customer and introducing JpaAttempt test helper --- README.md | 18 ++ .../db/changelog/2022-07-28-005-rbac-base.sql | 93 ++++--- .../db/changelog/2022-07-29-050-hs-base.sql | 25 +- .../2022-07-29-061-hs-customer-rbac.sql | 47 +++- .../2022-07-29-070-hs-package-rbac.sql | 2 +- .../resources/db/changelog/23-hs-unixuser.sql | 2 +- .../resources/db/changelog/24-hs-domain.sql | 2 +- .../db/changelog/25-hs-emailaddress.sql | 2 +- .../CustomerRepositoryIntegrationTest.java | 238 ++++++++++-------- .../java/net/hostsharing/test/JpaAttempt.java | 79 ++++++ 10 files changed, 363 insertions(+), 145 deletions(-) create mode 100644 src/test/java/net/hostsharing/test/JpaAttempt.java diff --git a/README.md b/README.md index 61b93490..1ddad2f4 100644 --- a/README.md +++ b/README.md @@ -227,3 +227,21 @@ You can explore the prototype as follows: (the example tables are currently not compatible with RBAC), - then run `historization.sql` in the database, - finally run `examples.sql` in the database. + +## How To + +### How to Use a Persistent Database for Integration Tests? + +Usually, the `DataJpaTest` integration tests run against a database in a temporary docker container. +As soon as the test ends, the database is gone; this might make debugging difficult. + +Alternatively + +If the persistent database and the temporary database show different results, one of these reasons could be the cause: + +1. You might have some changesets only running in either context, + check the `context: ...` in the changeset control lines. +2. You might have changes in the database which interfere with the tests, + e.g. from a previous run of tests or manually applied. + It's best to run `pg-sql-reset && gw bootRun` before each test run, to have a clean database. + diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index 8255ca2b..2929adec 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -1,6 +1,8 @@ --liquibase formatted sql ---changeset rbac-base-reference:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-base-REFERENCE:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -27,7 +29,9 @@ end; $$; --// ---changeset rbac-base-user:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-base-USER:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -86,7 +90,9 @@ $$; --// ---changeset rbac-base-object:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-base-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -119,7 +125,9 @@ end; $$; --// ---changeset rbac-base-role:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-base-ROLE:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -203,7 +211,9 @@ begin end; $$; ---changeset rbac-base-permission:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-base-PERMISSION:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -217,7 +227,6 @@ create domain RbacOp as varchar(67) or VALUE ~ '^add-[a-z]+$' ); --- DROP TABLE IF EXISTS RbacPermission; create table RbacPermission ( uuid uuid primary key references RbacReference (uuid) on delete cascade, @@ -226,11 +235,7 @@ create table RbacPermission unique (objectUuid, op) ); --- SET SESSION SESSION AUTHORIZATION DEFAULT; --- alter table rbacpermission add constraint rbacpermission_objectuuid_fkey foreign key (objectUuid) references rbacobject(uuid); --- alter table rbacpermission drop constraint rbacpermission_objectuuid; - -create or replace function hasPermission(forObjectUuid uuid, forOp RbacOp) +create or replace function permissionExists(forObjectUuid uuid, forOp RbacOp) returns bool language sql as $$ select exists( @@ -291,7 +296,9 @@ $$; --// ---changeset rbac-base-grants:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-base-GRANTS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -306,8 +313,6 @@ create index on RbacGrants (ascendantUuid); create index on RbacGrants (descendantUuid); ---// - create or replace function findGrantees(grantedId uuid) returns setof RbacReference returns null on null input @@ -377,7 +382,8 @@ begin insert into RbacGrants (ascendantUuid, descendantUuid, follow) - values (roleUuid, permissionIds[i], true); + values (roleUuid, permissionIds[i], true) + on conflict do nothing; -- allow granting multiple times end loop; end; $$; @@ -395,7 +401,7 @@ begin insert into RbacGrants (ascendantUuid, descendantUuid, follow) values (superRoleId, subRoleId, doFollow) - on conflict do nothing; -- TODO: remove? + on conflict do nothing; -- allow granting multiple times end; $$; create or replace procedure revokeRoleFromRole(subRoleId uuid, superRoleId uuid) @@ -418,11 +424,13 @@ begin insert into RbacGrants (ascendantUuid, descendantUuid, follow) values (userId, roleId, true) - on conflict do nothing; -- TODO: remove? + on conflict do nothing; -- allow granting multiple times end; $$; --// ---changeset rbac-base-query-accessible-object-uuids:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-base-QUERY-ACCESSIBLE-OBJECT-UUIDS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -467,7 +475,9 @@ $$; --// ---changeset rbac-base-query-granted-permissions:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-base-QUERY-GRANTED-PERMISSIONS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -494,7 +504,9 @@ $$; --// ---changeset rbac-base-query-users-with-permission-for-object:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-base-QUERY-USERS-WITH-PERMISSION-FOR-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -520,7 +532,9 @@ $$; --// ---changeset rbac-current-user:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-CURRENT-USER:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -553,13 +567,16 @@ declare begin currentUser := currentUser(); currentUserId = (select uuid from RbacUser where name = currentUser); + if currentUserId is null then + raise exception 'hsadminng.currentUser defined as %, but does not exists', currentUser; + end if; return currentUserId; end; $$; - - --// ---changeset rbac-assumed-roles:1 endDelimiter:--// +-- ============================================================================ +--changeset rbac-ASSUMED-ROLES:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- /* */ @@ -595,7 +612,7 @@ create or replace function findUuidByIdName(objectTable varchar, objectIdName va returns null on null input language plpgsql as $$ declare - sql varchar; + sql varchar; uuid uuid; begin objectTable := pureIdentifier(objectTable); @@ -604,10 +621,26 @@ begin begin raise notice 'sql: %', sql; execute sql into uuid; - exception when OTHERS then - raise exception 'function %UuidByIdName(...) not found, add identity view support for table %', objectTable, objectTable; + exception + when others then + raise exception 'function %UuidByIdName(...) not found, add identity view support for table %', objectTable, objectTable; end; return uuid; +end ; $$; + +create or replace function currentSubjects() + returns varchar(63)[] + stable leakproof + language plpgsql as $$ +declare + assumedRoles varchar(63)[]; +begin + assumedRoles := assumedRoles(); + if array_length(assumedRoles(), 1) > 0 then + return assumedRoles(); + else + return array[currentUser()]::varchar(63)[]; + end if; end; $$; create or replace function currentSubjectIds() @@ -664,9 +697,8 @@ end; $$; -- ============================================================================ --- PGSQL-ROLES ---changeset rbac-base-pgsql-roles:1 endDelimiter:--// --- ------------------------------------------------------------------ +--changeset rbac-base-PGSQL-ROLES:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- create role admin; grant all privileges on all tables in schema public to admin; @@ -675,3 +707,4 @@ create role restricted; grant all privileges on all tables in schema public to restricted; --// + diff --git a/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql b/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql index d1b42878..f8edf9a5 100644 --- a/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql +++ b/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql @@ -16,6 +16,8 @@ create table Global ); create unique index Global_Singleton on Global ((0)); +grant select on global to restricted; + /** A single row to be referenced as a global object. */ @@ -25,6 +27,23 @@ insert into Global (uuid, name) values ((select uuid from RbacObject where objectTable = 'global'), 'hostsharing'); --// + +-- ============================================================================ +--changeset rhs-base-HAS-GLOBAL-PERMISSION:1 endDelimiter:--// +-- ------------------------------------------------------------------ + +create or replace function hasGlobalPermission(op RbacOp) + returns boolean + language sql as +$$ + -- TODO: this could to be optimized +select (select uuid from global) in + (select queryAccessibleObjectUuidsOfSubjectIds( + op, 'global', currentSubjectIds())); +$$; +--// + + -- ============================================================================ --changeset hs-base-GLOBAL-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- @@ -34,7 +53,7 @@ insert */ drop view if exists global_iv; create or replace view global_iv as -select distinct target.uuid, target.name as idName +select target.uuid, target.name as idName from global as target; grant all privileges on global_iv to restricted; @@ -65,7 +84,7 @@ $$; select createRole(hostsharingAdmin()); -- ============================================================================ ---changeset hs-base-ADMIN-USERS:1 context:dev,test,tc endDelimiter:--// +--changeset hs-base-ADMIN-USERS:1 context:dev,tc endDelimiter:--// -- ---------------------------------------------------------------------------- /* Create two users and assign both to the administrators role. @@ -83,7 +102,7 @@ $$; -- ============================================================================ ---changeset hs-base-hostsharing-TEST:1 context:dev,test,tc runAlways:true endDelimiter:--// +--changeset hs-base-hostsharing-TEST:1 context:dev,tc runAlways:true endDelimiter:--// -- ---------------------------------------------------------------------------- /* diff --git a/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql index 3c7d3bba..c7baea62 100644 --- a/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql +++ b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql @@ -149,7 +149,7 @@ execute procedure deleteRbacRulesForCustomer(); */ drop view if exists customer_iv; create or replace view customer_iv as -select distinct target.uuid, target.prefix as idName +select target.uuid, target.prefix as idName from customer as target; -- TODO: Is it ok that everybody has access to this information? grant all privileges on customer_iv to restricted; @@ -176,8 +176,51 @@ $$; set session session authorization default; drop view if exists customer_rv; create or replace view customer_rv as -select distinct target.* +select target.* from customer as target where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'customer', currentSubjectIds())); grant all privileges on customer_rv to restricted; --// + + +-- ============================================================================ +--changeset hs-customer-rbac-ADD-CUSTOMER:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + Creates a global permission for add-customer and assigns it to the hostsharing admins role. + */ +do language plpgsql $$ + declare + addCustomerPermissions uuid[]; + hostsharingObjectUuid uuid; + hsAdminRoleUuid uuid ; + begin + hsAdminRoleUuid := findRoleId(hostsharingAdmin()); + hostsharingObjectUuid := (select uuid from global); + addCustomerPermissions := createPermissions(hostsharingObjectUuid, array ['add-customer']); + call grantPermissionsToRole(hsAdminRoleUuid, addCustomerPermissions); + end; +$$; + +/** + Used by the trigger to prevent the add-customer to current user respectively assumed roles. + */ +create or replace function addCustomerNotAllowedForCurrentSubjects() + returns trigger + language PLPGSQL +as $$ +begin + raise exception 'add-customer not permitted for %', array_to_string(currentSubjects()); +end; $$; + +/** + Checks if the user or assumed roles are allowed to add a new customer. + */ +create trigger customer_insert_trigger + before insert + on customer + for each row + when ( currentUser() <> 'mike@hostsharing.net' or not hasGlobalPermission('add-customer') ) +execute procedure addCustomerNotAllowedForCurrentSubjects(); +--// + diff --git a/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql index 8dc717f9..ea54d12a 100644 --- a/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql +++ b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql @@ -175,7 +175,7 @@ $$; */ drop view if exists package_rv; create or replace view package_rv as -select distinct target.* +select target.* from package as target where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'package', currentSubjectIds())); grant all privileges on package_rv to restricted; diff --git a/src/main/resources/db/changelog/23-hs-unixuser.sql b/src/main/resources/db/changelog/23-hs-unixuser.sql index 98e640e0..233ef5a8 100644 --- a/src/main/resources/db/changelog/23-hs-unixuser.sql +++ b/src/main/resources/db/changelog/23-hs-unixuser.sql @@ -115,7 +115,7 @@ set session session authorization default; -- ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; drop view if exists unixuser_rv; create or replace view unixuser_rv as -select distinct target.* +select target.* from unixuser as target where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'unixuser', currentSubjectIds())); grant all privileges on unixuser_rv to restricted; diff --git a/src/main/resources/db/changelog/24-hs-domain.sql b/src/main/resources/db/changelog/24-hs-domain.sql index ac7d7205..ece72c8a 100644 --- a/src/main/resources/db/changelog/24-hs-domain.sql +++ b/src/main/resources/db/changelog/24-hs-domain.sql @@ -100,7 +100,7 @@ set session session authorization default; -- ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; drop view if exists domain_rv; create or replace view domain_rv as -select distinct target.* +select target.* from Domain as target where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'domain', currentSubjectIds())); grant all privileges on domain_rv to restricted; diff --git a/src/main/resources/db/changelog/25-hs-emailaddress.sql b/src/main/resources/db/changelog/25-hs-emailaddress.sql index 97b884a7..9aa82621 100644 --- a/src/main/resources/db/changelog/25-hs-emailaddress.sql +++ b/src/main/resources/db/changelog/25-hs-emailaddress.sql @@ -85,7 +85,7 @@ set session session authorization default; -- ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; drop view if exists EMailAddress_rv; create or replace view EMailAddress_rv as -select distinct target.* +select target.* from EMailAddress as target where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'emailaddress', currentSubjectIds())); grant all privileges on EMailAddress_rv to restricted; diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java index 642405dd..b516948a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java @@ -6,15 +6,15 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.ComponentScan; -import org.springframework.core.NestedRuntimeException; import org.springframework.orm.jpa.JpaSystemException; import javax.persistence.EntityManager; +import javax.persistence.PersistenceException; import javax.transaction.Transactional; import java.util.List; -import java.util.Optional; -import java.util.function.Supplier; +import java.util.UUID; +import static net.hostsharing.test.JpaAttempt.attempt; import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest @@ -30,153 +30,179 @@ class CustomerRepositoryIntegrationTest { @Autowired EntityManager em; @Nested - class FindAll { - - private final Given given = new Given(); - private When> when; - private final Then then = new Then(); + class CreateCustomer { @Test - public void hostsharingAdminWithoutAssumedRoleCanViewAllCustomers() { - given.currentUser("mike@hostsharing.net"); + public void hostsharingAdmin_withoutAssumedRole_canCreateNewCustomer() { + // given + currentUser("mike@hostsharing.net"); - when(() -> customerRepository.findAll()); + // when + final var newCustomer = new CustomerEntity( + UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com"); + final var result = customerRepository.save(newCustomer); - then.exactlyTheseCustomersAreReturned("aaa", "aab", "aac"); + // then + assertThat(result).isNotNull().extracting(CustomerEntity::getUuid).isNotNull(); + assertThatCustomerIsPersisted(result); } @Test - public void hostsharingAdminWithAssumedHostsharingAdminRoleCanViewAllCustomers() { - given.currentUser("mike@hostsharing.net"). - and().assumedRoles("global#hostsharing.admin"); + public void hostsharingAdmin_withAssumedCustomerRole_cannotCreateNewCustomer() { + // given + currentUser("mike@hostsharing.net"); + assumedRoles("customer#aaa.admin"); - when(() -> customerRepository.findAll()); + // when + final var attempt = attempt(em, () -> { + final var newCustomer = new CustomerEntity( + UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com"); + return customerRepository.save(newCustomer); + }); - then.exactlyTheseCustomersAreReturned("aaa", "aab", "aac"); + // then + attempt.assertExceptionWithRootCauseMessage( + PersistenceException.class, + "add-customer not permitted for customer#aaa.admin"); } @Test - public void customerAdminWithoutAssumedRoleCanViewOnlyItsOwnCustomer() { - given.currentUser("admin@aaa.example.com"); + public void customerAdmin_withoutAssumedRole_cannotCreateNewCustomer() { + // given + currentUser("admin@aaa.example.com"); - when(() -> customerRepository.findAll()); + // when + final var attempt = attempt(em, () -> { + final var newCustomer = new CustomerEntity( + UUID.randomUUID(), "yyy", 90002, "admin@yyy.example.com"); + return customerRepository.save(newCustomer); + }); - then.exactlyTheseCustomersAreReturned("aaa"); + // then + attempt.assertExceptionWithRootCauseMessage( + PersistenceException.class, + "add-customer not permitted for admin@aaa.example.com"); + + } + + private void assertThatCustomerIsPersisted(final CustomerEntity saved) { + final var found = customerRepository.findById(saved.getUuid()); + assertThat(found).hasValue(saved); + } + } + + @Nested + class FindAllCustomers { + + @Test + public void hostsharingAdmin_withoutAssumedRole_canViewAllCustomers() { + // given + currentUser("mike@hostsharing.net"); + + // when + final var result = customerRepository.findAll(); + + // then + exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); } @Test - public void customerAdminWithAssumedOwnedPackageAdminRoleCanViewOnlyItsOwnCustomer() { - given.currentUser("admin@aaa.example.com"). - and().assumedRoles("package#aaa00.admin"); + public void hostsharingAdmin_withAssumedHostsharingAdminRole_canViewAllCustomers() { + given: + currentUser("mike@hostsharing.net"); + assumedRoles("global#hostsharing.admin"); - when(() -> customerRepository.findAll()); + // when + final var result = customerRepository.findAll(); - then.exactlyTheseCustomersAreReturned("aaa"); + then: + exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); + } + + @Test + public void customerAdmin_withoutAssumedRole_canViewOnlyItsOwnCustomer() { + // given: + currentUser("admin@aaa.example.com"); + + // when: + final var result = customerRepository.findAll(); + + // then: + exactlyTheseCustomersAreReturned(result, "aaa"); + } + + @Test + public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyItsOwnCustomer() { + currentUser("admin@aaa.example.com"); + assumedRoles("package#aaa00.admin"); + + final var result = customerRepository.findAll(); + + exactlyTheseCustomersAreReturned(result, "aaa"); } @Test public void customerAdmin_withAssumedAlienPackageAdminRole_cannotViewAnyCustomer() { - given.currentUser("admin@aaa.example.com"). - and().assumedRoles("package#aab00.admin"); + // given: + currentUser("admin@aaa.example.com"); + assumedRoles("package#aab00.admin"); - when(() -> customerRepository.findAll()); + // when + final var attempt = attempt( + em, + () -> customerRepository.findAll()); - then.expectJpaSystemExceptionHasBeenThrown(). - and() - .expectRootCauseMessageMatches( - ".* user admin@aaa.example.com .* has no permission to assume role package#aab00#admin .*"); + // then + attempt.assertExceptionWithRootCauseMessage( + JpaSystemException.class, + "user admin@aaa.example.com .* has no permission to assume role package#aab00#admin"); } @Test void unknownUser_withoutAssumedRole_cannotViewAnyCustomers() { - given.currentUser("unknown@example.org"); + currentUser("unknown@example.org"); - when(() -> customerRepository.findAll()); + final var attempt = attempt( + em, + () -> customerRepository.findAll()); - then.expectJpaSystemExceptionHasBeenThrown(). - and().expectRootCauseMessageMatches(".* user unknown@example.org does not exist.*"); + attempt.assertExceptionWithRootCauseMessage( + JpaSystemException.class, + "hsadminng.currentUser defined as unknown@example.org, but does not exists"); } @Test @Transactional - void unknownUserWithAssumedCustomerRoleCannotViewAnyCustomers() { - given.currentUser("unknown@example.org"). - and().assumedRoles("customer#aaa.admin"); + void unknownUser_withAssumedCustomerRole_cannotViewAnyCustomers() { + currentUser("unknown@example.org"); + assumedRoles("customer#aaa.admin"); - when(() -> customerRepository.findAll()); + final var attempt = attempt( + em, + () -> customerRepository.findAll()); - then.expectJpaSystemExceptionHasBeenThrown(). - and().expectRootCauseMessageMatches(".* user unknown@example.org does not exist.*"); + attempt.assertExceptionWithRootCauseMessage( + JpaSystemException.class, + "hsadminng.currentUser defined as unknown@example.org, but does not exists"); } - void when(final Supplier> code) { - try { - when = new When<>(code.get()); - } catch (final NestedRuntimeException exc) { - when = new When<>(exc); - } - } - - private class Then { - - Then and() { - return this; - } - - void exactlyTheseCustomersAreReturned(final String... customerPrefixes) { - assertThat(when.actualResult) - .hasSize(customerPrefixes.length) - .extracting(CustomerEntity::getPrefix) - .containsExactlyInAnyOrder(customerPrefixes); - } - - Then expectJpaSystemExceptionHasBeenThrown() { - assertThat(when.actualException).isInstanceOf(JpaSystemException.class); - return this; - } - - void expectRootCauseMessageMatches(final String expectedMessage) { - assertThat(firstRootCauseMessageLineOf(when.actualException)).matches(expectedMessage); - } - } } - private String firstRootCauseMessageLineOf(final NestedRuntimeException exception) { - return Optional.ofNullable(exception.getRootCause()) - .map(Throwable::getMessage) - .map(message -> message.split("\\r|\\n|\\r\\n", 0)[0]) - .orElse(null); + void currentUser(final String currentUser) { + context.setCurrentUser(currentUser); + assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser); } - private class Given { - - Given and() { - return this; - } - - Given currentUser(final String currentUser) { - context.setCurrentUser(currentUser); - assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser); - return this; - } - - void assumedRoles(final String assumedRoles) { - context.assumeRoles(assumedRoles); - assertThat(context.getAssumedRoles()).as("precondition").containsExactly(assumedRoles.split(";")); - } + void assumedRoles(final String assumedRoles) { + context.assumeRoles(assumedRoles); + assertThat(context.getAssumedRoles()).as("precondition").containsExactly(assumedRoles.split(";")); } - private static class When { - - T actualResult; - NestedRuntimeException actualException; - - When(final T actualResult) { - this.actualResult = actualResult; - } - - When(final NestedRuntimeException exception) { - this.actualException = exception; - } + void exactlyTheseCustomersAreReturned(final List actualResult, final String... customerPrefixes) { + assertThat(actualResult) + .hasSize(customerPrefixes.length) + .extracting(CustomerEntity::getPrefix) + .containsExactlyInAnyOrder(customerPrefixes); } + } diff --git a/src/test/java/net/hostsharing/test/JpaAttempt.java b/src/test/java/net/hostsharing/test/JpaAttempt.java new file mode 100644 index 00000000..358b1a6e --- /dev/null +++ b/src/test/java/net/hostsharing/test/JpaAttempt.java @@ -0,0 +1,79 @@ +package net.hostsharing.test; + +import junit.framework.AssertionFailedError; +import org.springframework.core.NestedExceptionUtils; + +import javax.persistence.EntityManager; +import java.util.Optional; +import java.util.function.Supplier; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Wraps the 'when' part of a DataJpaTest to improve readability of tests. + *

+ * It + * - makes sure that the SQL code is actually performed (em.flush()), + * - if any exception is throw, it's caught and stored, + * - makes the result available for assertions, + * - cleans the JPA first level cache to force assertions read from the database, not just cache, + * - offers some assertions based on the exception. + * * + * + * @param success result type + */ +public class JpaAttempt { + + private T result = null; + private RuntimeException exception = null; + + private String firstRootCauseMessageLineOf(final RuntimeException exception) { + final var rootCause = NestedExceptionUtils.getRootCause(exception); + return Optional.ofNullable(rootCause) + .map(Throwable::getMessage) + .map(message -> message.split("\\r|\\n|\\r\\n", 0)[0]) + .orElse(null); + } + + public static JpaAttempt attempt(final EntityManager em, final Supplier code) { + return new JpaAttempt<>(em, code); + } + + public JpaAttempt(final EntityManager em, final Supplier code) { + try { + result = code.get(); + em.flush(); + em.clear(); + } catch (RuntimeException exc) { + exception = exc; + } + } + + public boolean wasSuccessful() { + return exception == null; + } + + public T returnedResult() { + return result; + } + + public RuntimeException caughtException() { + return exception; + } + + @SuppressWarnings("unchecked") + public E caughtException(final Class expectedExceptionClass) { + if (expectedExceptionClass.isAssignableFrom(exception.getClass())) { + return (E) exception; + } + throw new AssertionFailedError("expected " + expectedExceptionClass + " but got " + exception); + } + + public void assertExceptionWithRootCauseMessage( + final Class expectedExceptionClass, + final String expectedRootCauseMessage) { + assertThat( + firstRootCauseMessageLineOf(caughtException(expectedExceptionClass))) + .matches(".*" + expectedRootCauseMessage + ".*"); + } +} From 2ac476d99b2d12edaf534e0e213c4dcc9137c50f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 2 Aug 2022 14:31:48 +0200 Subject: [PATCH 42/54] add-customer and introducing JpaAttempt test helper --- README.md | 38 +++++++++++-- .../RestResponseEntityExceptionHandler.java | 57 +++++++++++++++++++ .../hscustomer/CustomerController.java | 37 ++++++++---- .../hsadminng/hscustomer/CustomerEntity.java | 2 + .../2022-07-29-061-hs-customer-rbac.sql | 2 +- 5 files changed, 120 insertions(+), 16 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java diff --git a/README.md b/README.md index 1ddad2f4..e4bd4f02 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,20 @@ If you have at least Docker, the Java JDK and Gradle installed in appropriate ve # the following command should return a JSON array with just all packages visible for the admin of the customer aab: curl \ - -H 'current-user: mike@hostsharing.net' \ - -H 'assumed-roles: customer#aab.admin' \ + -H 'current-user: mike@hostsharing.net' -H 'assumed-roles: customer#aab.admin' \ http://localhost:8080/api/packages -The latter `curl` command actually goes through the database server. + # add a new customer + curl \ + -H 'current-user: mike@hostsharing.net' -H "Content-Type: application/json" \ + -d '{ "prefix":"baa", "reference":80001, "adminUserName":"admin@baa.example.com" }' \ + -X POST http://localhost:8080/api/customers - +**ⓘ** +'mike@hostsharing.net' and 'sven@hostsharing.net' are Hostsharing hostmaster accounts coming from the example data which is automatically inserted in Testcontainers and Development environments. +Also try for example 'admin@aaa.example.com' or 'unknown@example.org'. + +**ⓘ** If you want a formatted JSON output, you can pipe the result to `jq` or similar. If you still need to install some of these tools, find some hints in the next chapters. @@ -245,3 +252,26 @@ If the persistent database and the temporary database show different results, on e.g. from a previous run of tests or manually applied. It's best to run `pg-sql-reset && gw bootRun` before each test run, to have a clean database. +## How to Amend Liquibase SQL Changesets? + +Liquibase changesets are meant to be immutable and based on each other. +That means, once a changeset is written, it never changes, not even a whitespace or comment. +Liquibase is a *database migration tool*, not a *database initialization tool*. + +This, if you need to add change a table, stored procedure or whatever, +create a new changeset and apply `ALTER`, `DROP`, `CREATE OR REPLACE` or whatever SQL commands to perform your changes. +These changes will be automatically applied once the application starts up again. +This way, any staging or production database will always match the application code. + +But, during initial development that can be a big hassle because the database structure changes a lot in that stage. +Also, the actual structure of the database won't be easily recognized anymore through lots of migration changesets. + +Therefore, during initial development, it's good approach just to amend the existing changesets and delete the database: + +```shell +pg-sql-reset +gw bootRun +``` + +**⚠** +Just don't forget switching to the migration mode, once there is a production database! diff --git a/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java b/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java new file mode 100644 index 00000000..64ab4095 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java @@ -0,0 +1,57 @@ +package net.hostsharing.hsadminng.errors; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import org.springframework.core.NestedExceptionUtils; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.orm.jpa.JpaSystemException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import java.time.LocalDateTime; + +@ControllerAdvice +public class RestResponseEntityExceptionHandler + extends ResponseEntityExceptionHandler { + + @ExceptionHandler(DataIntegrityViolationException.class) + protected ResponseEntity handleConflict( + final RuntimeException exc, final WebRequest request) { + + return new ResponseEntity<>( + new CustomErrorResponse(exc, HttpStatus.CONFLICT), HttpStatus.CONFLICT); + } + + @ExceptionHandler(JpaSystemException.class) + protected ResponseEntity handleJpaExceptions( + final RuntimeException exc, final WebRequest request) { + + return new ResponseEntity<>( + new CustomErrorResponse(exc, HttpStatus.FORBIDDEN), HttpStatus.FORBIDDEN); + } +} + +@Getter +class CustomErrorResponse { + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private final LocalDateTime timestamp; + + private final HttpStatus status; + + private final String message; + + public CustomErrorResponse(final RuntimeException exc, final HttpStatus status) { + this.timestamp = LocalDateTime.now(); + this.status = status; + this.message = firstLine(NestedExceptionUtils.getMostSpecificCause(exc).getMessage()); + } + + private String firstLine(final String message) { + return message.split("\\r|\\n|\\r\\n", 0)[0]; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java index ffed4130..27aaf23a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java +++ b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java @@ -2,16 +2,14 @@ package net.hostsharing.hsadminng.hscustomer; import net.hostsharing.hsadminng.context.Context; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import javax.transaction.Transactional; import java.util.List; +import java.util.UUID; + +@RestController -@Controller public class CustomerController { @Autowired @@ -20,18 +18,35 @@ public class CustomerController { @Autowired private CustomerRepository customerRepository; - @ResponseBody - @RequestMapping(value = "/api/customers", method = RequestMethod.GET) + @GetMapping(value = "/api/customers") @Transactional public List listCustomers( - @RequestHeader(value = "current-user") String userName, - @RequestHeader(value="assumed-roles", required=false) String assumedRoles + @RequestHeader(value = "current-user") String userName, + @RequestHeader(value = "assumed-roles", required = false) String assumedRoles ) { context.setCurrentUser(userName); - if ( assumedRoles != null && !assumedRoles.isBlank() ) { + if (assumedRoles != null && !assumedRoles.isBlank()) { context.assumeRoles(assumedRoles); } return customerRepository.findAll(); } + @PostMapping(value = "/api/customers") + @ResponseStatus + @Transactional + public CustomerEntity addCustomer( + @RequestHeader(value = "current-user") String userName, + @RequestHeader(value = "assumed-roles", required = false) String assumedRoles, + @RequestBody CustomerEntity customer + ) { + context.setCurrentUser(userName); + if (assumedRoles != null && !assumedRoles.isBlank()) { + context.assumeRoles(assumedRoles); + } + if (customer.getUuid() == null) { + customer.setUuid(UUID.randomUUID()); + } + return customerRepository.save(customer); + } + } diff --git a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java index 8a5f99b3..ccc97488 100644 --- a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hscustomer; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import javax.persistence.*; import java.util.UUID; @@ -10,6 +11,7 @@ import java.util.UUID; @Entity @Table(name = "customer_rv") @Getter +@Setter @NoArgsConstructor @AllArgsConstructor public class CustomerEntity { diff --git a/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql index c7baea62..8524edab 100644 --- a/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql +++ b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql @@ -210,7 +210,7 @@ create or replace function addCustomerNotAllowedForCurrentSubjects() language PLPGSQL as $$ begin - raise exception 'add-customer not permitted for %', array_to_string(currentSubjects()); + raise exception 'add-customer not permitted for %', array_to_string(currentSubjects(), ';', 'null'); end; $$; /** From 142aac2bce7021574a0d2a4294ee900a23bfd9d5 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 2 Aug 2022 15:17:24 +0200 Subject: [PATCH 43/54] add ArchUnitTest --- build.gradle | 1 + .../hscustomer/CustomerRepository.java | 1 - .../hsadminng/arch/ArchUnitTest.java | 67 +++++++++++++++++++ .../CustomerControllerRestTest.java | 2 - 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/arch/ArchUnitTest.java diff --git a/build.gradle b/build.gradle index 79a36101..f2325df4 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,7 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:junit-jupiter' testImplementation 'org.testcontainers:postgresql' + testImplementation 'com.tngtech.archunit:archunit-junit5:1.0.0-rc1' } dependencyManagement { diff --git a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java index dca4dfb8..c98d6bac 100644 --- a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java @@ -1,7 +1,6 @@ package net.hostsharing.hsadminng.hscustomer; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.UUID; diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchUnitTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchUnitTest.java new file mode 100644 index 00000000..c54641d6 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchUnitTest.java @@ -0,0 +1,67 @@ +package net.hostsharing.hsadminng.arch; + +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchRule; +import org.junit.jupiter.api.Test; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.web.bind.annotation.RestController; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; +import static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices; + +@AnalyzeClasses(packages = ArchUnitTest.NET_HOSTSHARING_HSADMINNG) +public class ArchUnitTest { + + public static final String NET_HOSTSHARING_HSADMINNG = "net.hostsharing.hsadminng"; + + @ArchTest + @SuppressWarnings("unused") + public static final ArchRule contextPackageRule = classes() + .that().resideInAPackage("..context..") + .should().onlyDependOnClassesThat() + .resideOutsideOfPackage(NET_HOSTSHARING_HSADMINNG); + + @ArchTest + @SuppressWarnings("unused") + public static final ArchRule configPackageRule = classes() + .that().resideInAPackage("..config..") + .should().onlyDependOnClassesThat() + .resideOutsideOfPackage(NET_HOSTSHARING_HSADMINNG); + + @ArchTest + @SuppressWarnings("unused") + public static final ArchRule errorsPackageRule = classes() + .that().resideInAPackage("..errors..") + .should().onlyDependOnClassesThat() + .resideOutsideOfPackage(NET_HOSTSHARING_HSADMINNG); + + @ArchTest + @SuppressWarnings("unused") + public static final ArchRule hsPackagesRule = classes() + .that().resideInAPackage("..hs*") + .should().onlyBeAccessed().byClassesThat() + .resideInAnyPackage("..hs*"); + + @ArchTest + @SuppressWarnings("unused") + public static final ArchRule hsPackagePackageRule = classes() + .that().resideInAPackage("..hspackage..") + .should().onlyBeAccessed().byClassesThat() + .resideInAnyPackage("..hspackage.."); + + @Test + public void everythingShouldBeFreeOfCycles() { + slices().matching("net.hostsharing.hsadminng.(*)..").should().beFreeOfCycles(); + } + + @Test + public void restControllerNaming() { + classes().that().areAnnotatedWith(RestController.class).should().haveSimpleNameEndingWith("Controller"); + } + + @Test + public void repositoryNaming() { + classes().that().implement(JpaRepository.class).should().haveSimpleNameEndingWith("Repository"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java index 267a1c4a..26d24803 100644 --- a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java @@ -10,8 +10,6 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static java.util.UUID.randomUUID; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.*; From bb05eb4ac4f7d9c98ab87de47950fc90026902fa Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 3 Aug 2022 06:11:39 +0200 Subject: [PATCH 44/54] refactoring packages, moving Hostsharing related entities etc. to hs package --- .../RestResponseEntityExceptionHandler.java | 16 +++++++++++----- .../{ => hs}/hscustomer/CustomerController.java | 2 +- .../{ => hs}/hscustomer/CustomerEntity.java | 2 +- .../{ => hs}/hscustomer/CustomerRepository.java | 2 +- .../{ => hs}/hspackage/PackageController.java | 2 +- .../{ => hs}/hspackage/PackageEntity.java | 4 ++-- .../{ => hs}/hspackage/PackageRepository.java | 2 +- .../hostsharing/hsadminng/arch/ArchUnitTest.java | 8 ++++---- .../hscustomer/CustomerControllerRestTest.java | 2 +- .../hscustomer/CustomerControllerUnitTest.java | 2 +- .../CustomerRepositoryIntegrationTest.java | 4 +++- .../{ => hs}/hscustomer/TestCustomer.java | 4 +++- .../hspackage/PackageControllerRestTest.java | 5 +---- .../{ => hs}/hspackage/TestPackage.java | 7 ++++--- 14 files changed, 35 insertions(+), 27 deletions(-) rename src/main/java/net/hostsharing/hsadminng/{ => hs}/hscustomer/CustomerController.java (96%) rename src/main/java/net/hostsharing/hsadminng/{ => hs}/hscustomer/CustomerEntity.java (89%) rename src/main/java/net/hostsharing/hsadminng/{ => hs}/hscustomer/CustomerRepository.java (84%) rename src/main/java/net/hostsharing/hsadminng/{ => hs}/hspackage/PackageController.java (96%) rename src/main/java/net/hostsharing/hsadminng/{ => hs}/hspackage/PackageEntity.java (79%) rename src/main/java/net/hostsharing/hsadminng/{ => hs}/hspackage/PackageRepository.java (78%) rename src/test/java/net/hostsharing/hsadminng/{ => hs}/hscustomer/CustomerControllerRestTest.java (97%) rename src/test/java/net/hostsharing/hsadminng/{ => hs}/hscustomer/CustomerControllerUnitTest.java (97%) rename src/test/java/net/hostsharing/hsadminng/{ => hs}/hscustomer/CustomerRepositoryIntegrationTest.java (97%) rename src/test/java/net/hostsharing/hsadminng/{ => hs}/hscustomer/TestCustomer.java (79%) rename src/test/java/net/hostsharing/hsadminng/{ => hs}/hspackage/PackageControllerRestTest.java (94%) rename src/test/java/net/hostsharing/hsadminng/{ => hs}/hspackage/TestPackage.java (67%) diff --git a/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java b/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java index 64ab4095..f5e38ba7 100644 --- a/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java +++ b/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java @@ -23,7 +23,7 @@ public class RestResponseEntityExceptionHandler final RuntimeException exc, final WebRequest request) { return new ResponseEntity<>( - new CustomErrorResponse(exc, HttpStatus.CONFLICT), HttpStatus.CONFLICT); + new CustomErrorResponse(request.getContextPath(), exc, HttpStatus.CONFLICT), HttpStatus.CONFLICT); } @ExceptionHandler(JpaSystemException.class) @@ -31,7 +31,7 @@ public class RestResponseEntityExceptionHandler final RuntimeException exc, final WebRequest request) { return new ResponseEntity<>( - new CustomErrorResponse(exc, HttpStatus.FORBIDDEN), HttpStatus.FORBIDDEN); + new CustomErrorResponse(request.getContextPath(), exc, HttpStatus.FORBIDDEN), HttpStatus.FORBIDDEN); } } @@ -41,13 +41,19 @@ class CustomErrorResponse { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") private final LocalDateTime timestamp; - private final HttpStatus status; + private final String path; + + private final int status; + + private final String error; private final String message; - public CustomErrorResponse(final RuntimeException exc, final HttpStatus status) { + public CustomErrorResponse(final String path, final RuntimeException exc, final HttpStatus status) { this.timestamp = LocalDateTime.now(); - this.status = status; + this.path = path; + this.status = status.value(); + this.error = status.getReasonPhrase(); this.message = firstLine(NestedExceptionUtils.getMostSpecificCause(exc).getMessage()); } diff --git a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java similarity index 96% rename from src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java rename to src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java index 27aaf23a..ec13941b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.hscustomer; +package net.hostsharing.hsadminng.hs.hscustomer; import net.hostsharing.hsadminng.context.Context; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerEntity.java similarity index 89% rename from src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java rename to src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerEntity.java index ccc97488..174d2d83 100644 --- a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerEntity.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.hscustomer; +package net.hostsharing.hsadminng.hs.hscustomer; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java similarity index 84% rename from src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java rename to src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java index c98d6bac..d9f954aa 100644 --- a/src/main/java/net/hostsharing/hsadminng/hscustomer/CustomerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.hscustomer; +package net.hostsharing.hsadminng.hs.hscustomer; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageController.java similarity index 96% rename from src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java rename to src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageController.java index 19f0b48c..63b071e9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageController.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.hspackage; +package net.hostsharing.hsadminng.hs.hspackage; import net.hostsharing.hsadminng.context.Context; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageEntity.java similarity index 79% rename from src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java rename to src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageEntity.java index 0792e843..18117473 100644 --- a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageEntity.java @@ -1,9 +1,9 @@ -package net.hostsharing.hsadminng.hspackage; +package net.hostsharing.hsadminng.hs.hspackage; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -import net.hostsharing.hsadminng.hscustomer.CustomerEntity; +import net.hostsharing.hsadminng.hs.hscustomer.CustomerEntity; import javax.persistence.*; import java.util.UUID; diff --git a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageRepository.java similarity index 78% rename from src/main/java/net/hostsharing/hsadminng/hspackage/PackageRepository.java rename to src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageRepository.java index 0f3c8c38..125bd63a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hspackage/PackageRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageRepository.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.hspackage; +package net.hostsharing.hsadminng.hs.hspackage; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchUnitTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchUnitTest.java index c54641d6..156ce4e8 100644 --- a/src/test/java/net/hostsharing/hsadminng/arch/ArchUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchUnitTest.java @@ -39,16 +39,16 @@ public class ArchUnitTest { @ArchTest @SuppressWarnings("unused") public static final ArchRule hsPackagesRule = classes() - .that().resideInAPackage("..hs*") + .that().resideInAPackage("..hs.(*)..") .should().onlyBeAccessed().byClassesThat() - .resideInAnyPackage("..hs*"); + .resideInAnyPackage("..hs.(*).."); @ArchTest @SuppressWarnings("unused") public static final ArchRule hsPackagePackageRule = classes() - .that().resideInAPackage("..hspackage..") + .that().resideInAPackage("..hs.hspackage..") .should().onlyBeAccessed().byClassesThat() - .resideInAnyPackage("..hspackage.."); + .resideInAnyPackage("..hs.hspackage.."); @Test public void everythingShouldBeFreeOfCycles() { diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java similarity index 97% rename from src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java rename to src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java index 26d24803..ed1f1196 100644 --- a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.hscustomer; +package net.hostsharing.hsadminng.hs.hscustomer; import net.hostsharing.hsadminng.context.Context; import org.junit.jupiter.api.Test; diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java similarity index 97% rename from src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerUnitTest.java rename to src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java index 71bd0dab..01ccd926 100644 --- a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerControllerUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.hscustomer; +package net.hostsharing.hsadminng.hs.hscustomer; import net.hostsharing.hsadminng.context.Context; import org.junit.jupiter.api.Test; diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java similarity index 97% rename from src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java rename to src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java index b516948a..0e34c0e1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hscustomer/CustomerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java @@ -1,6 +1,8 @@ -package net.hostsharing.hsadminng.hscustomer; +package net.hostsharing.hsadminng.hs.hscustomer; import net.hostsharing.hsadminng.context.Context; +import net.hostsharing.hsadminng.hs.hscustomer.CustomerEntity; +import net.hostsharing.hsadminng.hs.hscustomer.CustomerRepository; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/TestCustomer.java similarity index 79% rename from src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java rename to src/test/java/net/hostsharing/hsadminng/hs/hscustomer/TestCustomer.java index f90b7e42..c77b0f91 100644 --- a/src/test/java/net/hostsharing/hsadminng/hscustomer/TestCustomer.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/TestCustomer.java @@ -1,4 +1,6 @@ -package net.hostsharing.hsadminng.hscustomer; +package net.hostsharing.hsadminng.hs.hscustomer; + +import net.hostsharing.hsadminng.hs.hscustomer.CustomerEntity; import static java.util.UUID.randomUUID; diff --git a/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hspackage/PackageControllerRestTest.java similarity index 94% rename from src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java rename to src/test/java/net/hostsharing/hsadminng/hs/hspackage/PackageControllerRestTest.java index 1100080c..bc7691f1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hspackage/PackageControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hspackage/PackageControllerRestTest.java @@ -1,7 +1,6 @@ -package net.hostsharing.hsadminng.hspackage; +package net.hostsharing.hsadminng.hs.hspackage; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.hscustomer.TestCustomer; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -10,8 +9,6 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import java.util.UUID; - import static java.util.Arrays.asList; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; diff --git a/src/test/java/net/hostsharing/hsadminng/hspackage/TestPackage.java b/src/test/java/net/hostsharing/hsadminng/hs/hspackage/TestPackage.java similarity index 67% rename from src/test/java/net/hostsharing/hsadminng/hspackage/TestPackage.java rename to src/test/java/net/hostsharing/hsadminng/hs/hspackage/TestPackage.java index 37651767..e586cc1c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hspackage/TestPackage.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hspackage/TestPackage.java @@ -1,7 +1,8 @@ -package net.hostsharing.hsadminng.hspackage; +package net.hostsharing.hsadminng.hs.hspackage; -import net.hostsharing.hsadminng.hscustomer.CustomerEntity; -import net.hostsharing.hsadminng.hscustomer.TestCustomer; +import net.hostsharing.hsadminng.hs.hscustomer.CustomerEntity; +import net.hostsharing.hsadminng.hs.hspackage.PackageEntity; +import net.hostsharing.hsadminng.hs.hscustomer.TestCustomer; import static java.util.UUID.randomUUID; From ece36209a56965829f19222ce67cc52f74b2329e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 3 Aug 2022 06:12:16 +0200 Subject: [PATCH 45/54] add RbacRole... --- .../rbac/rbacrole/RbacRoleController.java | 34 ++++++++++ .../rbac/rbacrole/RbacRoleEntity.java | 36 +++++++++++ .../rbac/rbacrole/RbacRoleRepository.java | 58 +++++++++++++++++ .../hsadminng/rbac/rbacrole/RbacRoleType.java | 5 ++ .../db/changelog/2022-07-28-005-rbac-base.sql | 62 +++++++++++++++++-- .../db/changelog/2022-07-29-050-hs-base.sql | 12 +++- .../2022-07-29-061-hs-customer-rbac.sql | 12 +++- .../2022-07-29-070-hs-package-rbac.sql | 10 +++ 8 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleController.java create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleController.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleController.java new file mode 100644 index 00000000..7a0c3864 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleController.java @@ -0,0 +1,34 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +import net.hostsharing.hsadminng.context.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +import javax.transaction.Transactional; + +@RestController + +public class RbacRoleController { + + @Autowired + private Context context; + + @Autowired + private RbacRoleRepository rbacRoleRepository; + + @GetMapping(value = "/api/rbacroles") + @Transactional + public Iterable listCustomers( + @RequestHeader(value = "current-user") String userName, + @RequestHeader(value = "assumed-roles", required = false) String assumedRoles + ) { + context.setCurrentUser(userName); + if (assumedRoles != null && !assumedRoles.isBlank()) { + context.assumeRoles(assumedRoles); + } + return rbacRoleRepository.findAll(); + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java new file mode 100644 index 00000000..0b343d8c --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java @@ -0,0 +1,36 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.annotation.Immutable; + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Table(name = "rbacrole_rv") +@Getter +@Setter +@Immutable +@NoArgsConstructor +@AllArgsConstructor +public class RbacRoleEntity { + + @Id + private UUID uuid; + + @Column(name="objectuuid") + private UUID objectUuid; + + @Column(name="roletype") + @Enumerated(EnumType.STRING) + private RbacRoleType roleType; + + @Column(name="objecttable") + private String objectTable; + + @Column(name="objectidname") + private String objectIdName; +} diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java new file mode 100644 index 00000000..1df718fb --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java @@ -0,0 +1,58 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.repository.Repository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface RbacRoleRepository extends Repository { + + /** + * Retrieves an entity by its id. + * + * @param id must not be {@literal null}. + * @return the entity with the given id or {@literal Optional#empty()} if none found. + * @throws IllegalArgumentException if {@literal id} is {@literal null}. + */ + Optional findByUuid(UUID id); + + /** + * Returns whether an entity with the given id exists. + * + * @param id must not be {@literal null}. + * @return {@literal true} if an entity with the given id exists, {@literal false} otherwise. + * @throws IllegalArgumentException if {@literal id} is {@literal null}. + */ + boolean existsByUuid(RbacRoleEntity id); + + /** + * Returns all instances of the type. + * + * @return all entities + */ + Iterable findAll(); + + /** + * Returns all entities sorted by the given options. + * + * @param sort the {@link Sort} specification to sort the results by, can be {@link Sort#unsorted()}, must not be + * {@literal null}. + * @return all entities sorted by the given options + */ + List findAll(Sort sort); + + /** + * Returns a {@link Page} of entities meeting the paging restriction provided in the {@link Pageable} object. + * + * @param pageable the pageable to request a paged result, can be {@link Pageable#unpaged()}, must not be + * {@literal null}. + * @return a page of entities + */ + Page findAll(Pageable pageable); + +} + diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java new file mode 100644 index 00000000..51d58bc6 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java @@ -0,0 +1,5 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +public enum RbacRoleType { + owner, admin, tenant +} diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index 2929adec..3666b4b6 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -151,13 +151,11 @@ create type RbacRoleDescriptor as create or replace function roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType) returns RbacRoleDescriptor returns null on null input - -- STABLE LEAKPROOF + stable leakproof language sql as $$ select objectTable, objectUuid, roleType::RbacRoleType; $$; - - create or replace function createRole(roleDescriptor RbacRoleDescriptor) returns uuid returns null on null input @@ -347,6 +345,22 @@ select granteeId = grantedId or granteeId in (with recursive grants as (select d from grants); $$; +create or replace function isGranted(granteeIds uuid[], grantedId uuid) + returns bool + returns null on null input + language plpgsql as $$ +declare + granteeId uuid; +begin + -- TODO: needs optimization + foreach granteeId in array granteeIds loop + if isGranted(granteeId, grantedId) then + return true; + end if; + end loop; + return false; +end; $$; + create or replace function isPermissionGrantedToSubject(permissionId uuid, subjectId uuid) returns BOOL stable leakproof @@ -607,6 +621,7 @@ begin return regexp_replace(rawIdentifier, '\W+', ''); end; $$; +-- TODO: rename to findObjectUuidByIdName create or replace function findUuidByIdName(objectTable varchar, objectIdName varchar) returns uuid returns null on null input @@ -628,18 +643,38 @@ begin return uuid; end ; $$; +create or replace function findIdNameByObjectUuid(objectTable varchar, objectUuid uuid) + returns varchar + returns null on null input + language plpgsql as $$ +declare + sql varchar; + idName varchar; +begin + objectTable := pureIdentifier(objectTable); + sql := format('select * from %sIdNameByUuid(%L::uuid);', objectTable, objectUuid); + begin + raise notice 'sql: %', sql; + execute sql into idName; + exception + when others then + raise exception 'function %IdNameByUuid(...) not found, add identity view support for table %', objectTable, objectTable; + end; + return idName; +end ; $$; + create or replace function currentSubjects() returns varchar(63)[] stable leakproof language plpgsql as $$ declare - assumedRoles varchar(63)[]; + assumedRoles varchar(63)[]; begin assumedRoles := assumedRoles(); if array_length(assumedRoles(), 1) > 0 then return assumedRoles(); else - return array[currentUser()]::varchar(63)[]; + return array [currentUser()]::varchar(63)[]; end if; end; $$; @@ -708,3 +743,20 @@ grant all privileges on all tables in schema public to restricted; --// + +-- ============================================================================ +--changeset rbac-base-ROLE-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + Creates a view to the role table with row-level limitation + based on the grants of the current user or assumed roles. + */ +drop view if exists rbacrole_rv; +create or replace view rbacrole_rv as +select r.*, o.objectTable, + findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName + from rbacrole as r + join rbacobject as o on o.uuid=r.objectuuid + where isGranted(currentSubjectIds(), r.uuid); +grant all privileges on rbacrole_rv to restricted; +--// diff --git a/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql b/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql index f8edf9a5..82cd5fe4 100644 --- a/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql +++ b/src/main/resources/db/changelog/2022-07-29-050-hs-base.sql @@ -58,7 +58,7 @@ select target.uuid, target.name as idName grant all privileges on global_iv to restricted; /* - Returns the objectUuid for a given identifying name (in this case the prefix). + Returns the objectUuid for a given identifying name (in this case the idName). */ create or replace function globalUuidByIdName(idName varchar) returns uuid @@ -66,6 +66,16 @@ create or replace function globalUuidByIdName(idName varchar) strict as $$ select uuid from global_iv iv where iv.idName = globalUuidByIdName.idName; $$; + +/* + Returns the identifying name for a given objectUuid (in this case the idName). + */ +create or replace function globalIdNameByUuid(uuid uuid) + returns varchar + language sql + strict as $$ +select idName from global_iv iv where iv.uuid = globalIdNameByUuid.uuid; +$$; --// -- ============================================================================ diff --git a/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql index 8524edab..5afa8dfb 100644 --- a/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql +++ b/src/main/resources/db/changelog/2022-07-29-061-hs-customer-rbac.sql @@ -163,6 +163,16 @@ create or replace function customerUuidByIdName(idName varchar) strict as $$ select uuid from customer_iv iv where iv.idName = customerUuidByIdName.idName; $$; + +/* + Returns the identifying name for a given objectUuid (in this case the prefix). + */ +create or replace function customerIdNameByUuid(uuid uuid) + returns varchar + language sql + strict as $$ +select idName from customer_iv iv where iv.uuid = customerIdNameByUuid.uuid; +$$; --// @@ -170,7 +180,7 @@ $$; --changeset hs-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- /* - Creates a view to the customer main table with row-level limitatation + Creates a view to the customer main table with row-level limitation based on the 'view' permission of the current user or assumed roles. */ set session session authorization default; diff --git a/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql index ea54d12a..f5b8263d 100644 --- a/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql +++ b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql @@ -162,6 +162,16 @@ create or replace function packageUuidByIdName(idName varchar) strict as $$ select uuid from package_iv iv where iv.idName = packageUuidByIdName.idName; $$; + +/* + Returns the identifying name for a given objectUuid (in this case the name). + */ +create or replace function packageIdNameByUuid(uuid uuid) + returns varchar + language sql + strict as $$ +select idName from package_iv iv where iv.uuid = packageIdNameByUuid.uuid; +$$; --// From dc6445544a9e906205734848df016dbfd763155f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 3 Aug 2022 08:52:45 +0200 Subject: [PATCH 46/54] add RbacRoleEntity.roleName as computed column --- .../hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java index 0b343d8c..c497019e 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java @@ -4,6 +4,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.hibernate.annotations.Formula; import org.springframework.data.annotation.Immutable; import javax.persistence.*; @@ -33,4 +34,7 @@ public class RbacRoleEntity { @Column(name="objectidname") private String objectIdName; + + @Formula("objectTable||'#'||objectIdName||'.'||roleType") + private String roleName; } From 5f08b4172469c812c9df4f30a38e70956f8171f2 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 3 Aug 2022 09:06:21 +0200 Subject: [PATCH 47/54] add SwaggerUI --- README.md | 7 ++++--- build.gradle | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e4bd4f02..319988be 100644 --- a/README.md +++ b/README.md @@ -60,13 +60,14 @@ If you have at least Docker, the Java JDK and Gradle installed in appropriate ve -d '{ "prefix":"baa", "reference":80001, "adminUserName":"admin@baa.example.com" }' \ -X POST http://localhost:8080/api/customers -**ⓘ** -'mike@hostsharing.net' and 'sven@hostsharing.net' are Hostsharing hostmaster accounts coming from the example data which is automatically inserted in Testcontainers and Development environments. +If you wonder who 'mike@hostsharing.net' and 'sven@hostsharing.net' are and where the data comes from: +Mike and Sven are just example Hostsharing hostmaster accounts as part of the example data which is automatically inserted in Testcontainers and Development environments. Also try for example 'admin@aaa.example.com' or 'unknown@example.org'. -**ⓘ** If you want a formatted JSON output, you can pipe the result to `jq` or similar. +And to see the full, currently implemented, API, open http://localhost:8080/swagger-ui/index.html. + If you still need to install some of these tools, find some hints in the next chapters. diff --git a/build.gradle b/build.gradle index f2325df4..22b61874 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-hateoas' implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springdoc:springdoc-openapi-ui:1.6.9' implementation 'org.liquibase:liquibase-core' implementation 'org.springframework.data:spring-data-rest-hal-explorer' implementation 'com.vladmihalcea:hibernate-types-55:2.17.1' From 8fb92f9978f1a962879376f98088cb8bd1d0e200 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 3 Aug 2022 09:07:02 +0200 Subject: [PATCH 48/54] remove hateoas and hal explorer for now --- build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle b/build.gradle index 22b61874..575110be 100644 --- a/build.gradle +++ b/build.gradle @@ -25,12 +25,10 @@ ext { dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-rest' - implementation 'org.springframework.boot:spring-boot-starter-hateoas' implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springdoc:springdoc-openapi-ui:1.6.9' implementation 'org.liquibase:liquibase-core' - implementation 'org.springframework.data:spring-data-rest-hal-explorer' implementation 'com.vladmihalcea:hibernate-types-55:2.17.1' compileOnly 'org.projectlombok:lombok' From 57cf316c00833a4630b5c308359574d7a85f6509 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 4 Aug 2022 09:09:06 +0200 Subject: [PATCH 49/54] for customer findAll()->findCustomerByOptionalPrefix(...) --- .../hs/hscustomer/CustomerController.java | 6 +- .../hs/hscustomer/CustomerRepository.java | 16 ++++- .../db/changelog/2022-07-28-005-rbac-base.sql | 22 ++++--- .../CustomerControllerRestTest.java | 27 ++++++-- .../CustomerControllerUnitTest.java | 8 +-- .../CustomerRepositoryIntegrationTest.java | 62 ++++++++++++++----- 6 files changed, 104 insertions(+), 37 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java index ec13941b..e3b2362a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java @@ -22,13 +22,14 @@ public class CustomerController { @Transactional public List listCustomers( @RequestHeader(value = "current-user") String userName, - @RequestHeader(value = "assumed-roles", required = false) String assumedRoles + @RequestHeader(value = "assumed-roles", required = false) String assumedRoles, + @RequestParam(required = false) String prefix ) { context.setCurrentUser(userName); if (assumedRoles != null && !assumedRoles.isBlank()) { context.assumeRoles(assumedRoles); } - return customerRepository.findAll(); + return customerRepository.findCustomerByOptionalPrefix(prefix); } @PostMapping(value = "/api/customers") @@ -48,5 +49,4 @@ public class CustomerController { } return customerRepository.save(customer); } - } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java index d9f954aa..51ed90d9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java @@ -1,11 +1,21 @@ package net.hostsharing.hsadminng.hs.hscustomer; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.query.Param; import java.util.List; +import java.util.Optional; import java.util.UUID; -public interface CustomerRepository extends JpaRepository { +public interface CustomerRepository extends Repository { + + + Optional findByUuid(UUID id); + + @Query("SELECT c FROM CustomerEntity c WHERE :prefix is null or c.prefix like concat(:prefix, '%')") + List findCustomerByOptionalPrefix(@Param("prefix") String prefix); + + CustomerEntity save(final CustomerEntity entity); - List findByPrefixLike(final String prefix); } diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index 3666b4b6..6b5819fd 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -111,11 +111,18 @@ declare objectUuid uuid; begin if TG_OP = 'INSERT' then - insert - into RbacObject (objectTable) - values (TG_TABLE_NAME) - returning uuid into objectUuid; - NEW.uuid = objectUuid; + if NEW.uuid is null then + insert + into RbacObject (objectTable) + values (TG_TABLE_NAME) + returning uuid into objectUuid; + NEW.uuid = objectUuid; + else + insert + into RbacObject (uuid, objectTable) + values (NEW.uuid, TG_TABLE_NAME) + returning uuid into objectUuid; + end if; return NEW; else raise exception 'invalid usage of TRIGGER AFTER INSERT'; @@ -138,7 +145,8 @@ create table RbacRole ( uuid uuid primary key references RbacReference (uuid) on delete cascade, objectUuid uuid references RbacObject (uuid) not null, - roleType RbacRoleType not null + roleType RbacRoleType not null, + unique (objectUuid, roleType) ); create type RbacRoleDescriptor as @@ -753,7 +761,7 @@ grant all privileges on all tables in schema public to restricted; */ drop view if exists rbacrole_rv; create or replace view rbacrole_rv as -select r.*, o.objectTable, +select DISTINCT r.*, o.objectTable, findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName from rbacrole as r join rbacobject as o on o.uuid=r.objectuuid diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java index ed1f1196..c646d389 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java @@ -12,7 +12,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -27,13 +27,13 @@ class CustomerControllerRestTest { CustomerRepository customerRepositoryMock; @Test - void apiCustomersWillReturnCustomersFromRepository() throws Exception { + void apiCustomersWillReturnAllCustomersFromRepositoryIfNoCriteriaGiven() throws Exception { // given - when(customerRepositoryMock.findAll()).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); + when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); // when - final var pacs = mockMvc.perform(MockMvcRequestBuilders + mockMvc.perform(MockMvcRequestBuilders .get("/api/customers") .header("current-user", "mike@hostsharing.net") .accept(MediaType.APPLICATION_JSON)) @@ -44,4 +44,23 @@ class CustomerControllerRestTest { .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix()))) .andExpect(jsonPath("$[1].reference", is(TestCustomer.yyy.getReference()))); } + + @Test + void apiCustomersWillReturnMatchingCustomersFromRepositoryIfCriteriaGiven() throws Exception { + + // given + when(customerRepositoryMock.findCustomerByOptionalPrefix("x")).thenReturn(asList(TestCustomer.xxx)); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/customers") + .header("current-user", "mike@hostsharing.net") + .param("prefix", "x") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix()))); + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java index 01ccd926..30541c50 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java @@ -27,10 +27,10 @@ class CustomerControllerUnitTest { void apiCustomersWillReturnCustomersFromRepository() throws Exception { // given - when(customerRepositoryMock.findAll()).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); + when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); // when - final var pacs = customerController.listCustomers("mike@hostsharing.net", null); + final var pacs = customerController.listCustomers("mike@hostsharing.net", null, null); // then assertThat(pacs).hasSize(2); @@ -42,10 +42,10 @@ class CustomerControllerUnitTest { void findAllWithAssumedCustomerAdminRole() throws Exception { // given - when(customerRepositoryMock.findAll()).thenReturn(singletonList(TestCustomer.yyy)); + when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(singletonList(TestCustomer.yyy)); // when - final var pacs = customerController.listCustomers("mike@hostsharing.net", "customer#yyy.admin"); + final var pacs = customerController.listCustomers("mike@hostsharing.net", "customer#yyy.admin", null); // then assertThat(pacs).hasSize(1); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java index 0e34c0e1..7c510b0d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java @@ -1,8 +1,6 @@ package net.hostsharing.hsadminng.hs.hscustomer; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.hs.hscustomer.CustomerEntity; -import net.hostsharing.hsadminng.hs.hscustomer.CustomerRepository; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -40,13 +38,17 @@ class CustomerRepositoryIntegrationTest { currentUser("mike@hostsharing.net"); // when - final var newCustomer = new CustomerEntity( - UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com"); - final var result = customerRepository.save(newCustomer); + + final var attempt = attempt(em, () -> { + final var newCustomer = new CustomerEntity( + UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com"); + return customerRepository.save(newCustomer); + }); // then - assertThat(result).isNotNull().extracting(CustomerEntity::getUuid).isNotNull(); - assertThatCustomerIsPersisted(result); + assertThat(attempt.wasSuccessful()).isTrue(); + assertThat(attempt.returnedResult()).isNotNull().extracting(CustomerEntity::getUuid).isNotNull(); + assertThatCustomerIsPersisted(attempt.returnedResult()); } @Test @@ -88,8 +90,8 @@ class CustomerRepositoryIntegrationTest { } private void assertThatCustomerIsPersisted(final CustomerEntity saved) { - final var found = customerRepository.findById(saved.getUuid()); - assertThat(found).hasValue(saved); + final var found = customerRepository.findByUuid(saved.getUuid()); + assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved); } } @@ -102,7 +104,7 @@ class CustomerRepositoryIntegrationTest { currentUser("mike@hostsharing.net"); // when - final var result = customerRepository.findAll(); + final var result = customerRepository.findCustomerByOptionalPrefix(null); // then exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); @@ -115,7 +117,7 @@ class CustomerRepositoryIntegrationTest { assumedRoles("global#hostsharing.admin"); // when - final var result = customerRepository.findAll(); + final var result = customerRepository.findCustomerByOptionalPrefix(null); then: exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); @@ -127,7 +129,7 @@ class CustomerRepositoryIntegrationTest { currentUser("admin@aaa.example.com"); // when: - final var result = customerRepository.findAll(); + final var result = customerRepository.findCustomerByOptionalPrefix(null); // then: exactlyTheseCustomersAreReturned(result, "aaa"); @@ -138,7 +140,7 @@ class CustomerRepositoryIntegrationTest { currentUser("admin@aaa.example.com"); assumedRoles("package#aaa00.admin"); - final var result = customerRepository.findAll(); + final var result = customerRepository.findCustomerByOptionalPrefix(null); exactlyTheseCustomersAreReturned(result, "aaa"); } @@ -152,7 +154,7 @@ class CustomerRepositoryIntegrationTest { // when final var attempt = attempt( em, - () -> customerRepository.findAll()); + () -> customerRepository.findCustomerByOptionalPrefix(null)); // then attempt.assertExceptionWithRootCauseMessage( @@ -166,7 +168,7 @@ class CustomerRepositoryIntegrationTest { final var attempt = attempt( em, - () -> customerRepository.findAll()); + () -> customerRepository.findCustomerByOptionalPrefix(null)); attempt.assertExceptionWithRootCauseMessage( JpaSystemException.class, @@ -181,7 +183,7 @@ class CustomerRepositoryIntegrationTest { final var attempt = attempt( em, - () -> customerRepository.findAll()); + () -> customerRepository.findCustomerByOptionalPrefix(null)); attempt.assertExceptionWithRootCauseMessage( JpaSystemException.class, @@ -190,6 +192,34 @@ class CustomerRepositoryIntegrationTest { } + @Nested + class FindByPrefixLike { + + @Test + public void hostsharingAdmin_withoutAssumedRole_canViewAllCustomers() { + // given + currentUser("mike@hostsharing.net"); + + // when + final var result = customerRepository.findCustomerByOptionalPrefix("aab"); + + // then + exactlyTheseCustomersAreReturned(result, "aab"); + } + + @Test + public void customerAdmin_withoutAssumedRole_canViewOnlyItsOwnCustomer() { + // given: + currentUser("admin@aaa.example.com"); + + // when: + final var result = customerRepository.findCustomerByOptionalPrefix("aab"); + + // then: + exactlyTheseCustomersAreReturned(result); + } + } + void currentUser(final String currentUser) { context.setCurrentUser(currentUser); assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser); From 212b1077c8483d5e6c881cdb068c89a3afe6a9d6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 4 Aug 2022 09:11:11 +0200 Subject: [PATCH 50/54] adds RbacRoleControllerRestTest + RbacRoleRepositoryIntegrationTest + fix duplicate key --- .../rbac/rbacrole/RbacRoleEntity.java | 14 +- .../rbac/rbacrole/RbacRoleRepository.java | 43 +---- .../2022-07-28-020-rbac-role-builder.sql | 1 - .../2022-07-29-070-hs-package-rbac.sql | 2 +- .../2022-07-29-070-hs-package-test-data.sql | 3 +- .../rbacrole/RbacRoleControllerRestTest.java | 54 ++++++ .../RbacRoleRepositoryIntegrationTest.java | 169 ++++++++++++++++++ .../hsadminng/rbac/rbacrole/TestRbacRole.java | 14 ++ 8 files changed, 246 insertions(+), 54 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerRestTest.java create mode 100644 src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepositoryIntegrationTest.java create mode 100644 src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java index c497019e..d0dc4d93 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java @@ -1,9 +1,6 @@ package net.hostsharing.hsadminng.rbac.rbacrole; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; import org.hibernate.annotations.Formula; import org.springframework.data.annotation.Immutable; @@ -14,6 +11,7 @@ import java.util.UUID; @Table(name = "rbacrole_rv") @Getter @Setter +@ToString @Immutable @NoArgsConstructor @AllArgsConstructor @@ -25,16 +23,16 @@ public class RbacRoleEntity { @Column(name="objectuuid") private UUID objectUuid; - @Column(name="roletype") - @Enumerated(EnumType.STRING) - private RbacRoleType roleType; - @Column(name="objecttable") private String objectTable; @Column(name="objectidname") private String objectIdName; + @Column(name="roletype") + @Enumerated(EnumType.STRING) + private RbacRoleType roleType; + @Formula("objectTable||'#'||objectIdName||'.'||roleType") private String roleName; } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java index 1df718fb..0af5091c 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java @@ -1,58 +1,17 @@ package net.hostsharing.hsadminng.rbac.rbacrole; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; import org.springframework.data.repository.Repository; import java.util.List; -import java.util.Optional; import java.util.UUID; public interface RbacRoleRepository extends Repository { - /** - * Retrieves an entity by its id. - * - * @param id must not be {@literal null}. - * @return the entity with the given id or {@literal Optional#empty()} if none found. - * @throws IllegalArgumentException if {@literal id} is {@literal null}. - */ - Optional findByUuid(UUID id); - - /** - * Returns whether an entity with the given id exists. - * - * @param id must not be {@literal null}. - * @return {@literal true} if an entity with the given id exists, {@literal false} otherwise. - * @throws IllegalArgumentException if {@literal id} is {@literal null}. - */ - boolean existsByUuid(RbacRoleEntity id); - /** * Returns all instances of the type. * * @return all entities */ - Iterable findAll(); - - /** - * Returns all entities sorted by the given options. - * - * @param sort the {@link Sort} specification to sort the results by, can be {@link Sort#unsorted()}, must not be - * {@literal null}. - * @return all entities sorted by the given options - */ - List findAll(Sort sort); - - /** - * Returns a {@link Page} of entities meeting the paging restriction provided in the {@link Pageable} object. - * - * @param pageable the pageable to request a paged result, can be {@link Pageable#unpaged()}, must not be - * {@literal null}. - * @return a page of entities - */ - Page findAll(Pageable pageable); - + List findAll(); } diff --git a/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql index 35ec5ea8..255b6ccb 100644 --- a/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql +++ b/src/main/resources/db/changelog/2022-07-28-020-rbac-role-builder.sql @@ -179,7 +179,6 @@ declare userUuid uuid; begin raise notice 'will createRole for %', roleDescriptor; - raise notice 'will createRole for % % %', roleDescriptor.objecttable, roleDescriptor.objectuuid, roleDescriptor.roletype; roleUuid = createRole(roleDescriptor); call grantPermissionsToRole(roleUuid, permissions.permissionUuids); diff --git a/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql index f5b8263d..d7588ec4 100644 --- a/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql +++ b/src/main/resources/db/changelog/2022-07-29-070-hs-package-rbac.sql @@ -24,7 +24,7 @@ create or replace function packageOwner(pac package) returns null on null input language plpgsql as $$ begin - return roleDescriptor('package', pac.uuid, 'admin'); + return roleDescriptor('package', pac.uuid, 'owner'); end; $$; create or replace function packageAdmin(pac package) diff --git a/src/main/resources/db/changelog/2022-07-29-070-hs-package-test-data.sql b/src/main/resources/db/changelog/2022-07-29-070-hs-package-test-data.sql index 6e08b1e0..78274594 100644 --- a/src/main/resources/db/changelog/2022-07-29-070-hs-package-test-data.sql +++ b/src/main/resources/db/changelog/2022-07-29-070-hs-package-test-data.sql @@ -23,7 +23,7 @@ create or replace procedure createPackageTestData( loop CONTINUE WHEN cust.reference < minCustomerReference; - for t in 0..randominrange(1, 2) + for t in 0..2 loop pacName = cust.prefix || to_char(t, 'fm00'); currentTask = 'creating RBAC test package #' || pacName || ' for customer ' || cust.prefix || ' #' || @@ -59,4 +59,3 @@ do language plpgsql $$ end; $$; --// - diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerRestTest.java new file mode 100644 index 00000000..f80599d3 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerRestTest.java @@ -0,0 +1,54 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +import net.hostsharing.hsadminng.context.Context; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static java.util.Arrays.asList; +import static net.hostsharing.hsadminng.rbac.rbacrole.TestRbacRole.*; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(RbacRoleController.class) +class RbacRoleControllerRestTest { + + @Autowired + MockMvc mockMvc; + @MockBean + Context contextMock; + @MockBean + RbacRoleRepository rbacRoleRepository; + + @Test + void apiCustomersWillReturnCustomersFromRepository() throws Exception { + + // given + when(rbacRoleRepository.findAll()).thenReturn( + asList(hostmasterRole, customerXxxOwner, customerXxxAdmin)); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/rbacroles") + .header("current-user", "mike@hostsharing.net") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(3))) + .andExpect(jsonPath("$[0].roleName", is("global#hostsharing.admin"))) + .andExpect(jsonPath("$[1].roleName", is("customer#xxx.owner"))) + .andExpect(jsonPath("$[2].roleName", is("customer#xxx.admin"))) + .andExpect(jsonPath("$[2].uuid", is(customerXxxAdmin.getUuid().toString()))) + .andExpect(jsonPath("$[2].objectUuid", is(customerXxxAdmin.getObjectUuid().toString()))) + .andExpect(jsonPath("$[2].objectTable", is(customerXxxAdmin.getObjectTable().toString()))) + .andExpect(jsonPath("$[2].objectIdName", is(customerXxxAdmin.getObjectIdName().toString()))); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepositoryIntegrationTest.java new file mode 100644 index 00000000..5542bb9f --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepositoryIntegrationTest.java @@ -0,0 +1,169 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +import net.hostsharing.hsadminng.context.Context; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.orm.jpa.JpaSystemException; + +import javax.persistence.EntityManager; +import javax.transaction.Transactional; + +import static net.hostsharing.test.JpaAttempt.attempt; +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@ComponentScan(basePackageClasses = { Context.class, RbacRoleRepository.class }) +class RbacRoleRepositoryIntegrationTest { + + @Autowired + Context context; + + @Autowired + RbacRoleRepository rbacRoleRepository; + + @Autowired EntityManager em; + + @Nested + class FindAllRbacRoles { + + private static final String[] ALL_TEST_DATA_ROLES = new String[] { + // @formatter:off + "global#hostsharing.admin", + "customer#aaa.admin", "customer#aaa.owner", "customer#aaa.tenant", + "package#aaa00.admin", "package#aaa00.owner", "package#aaa00.tenant", + "package#aaa01.admin", "package#aaa01.owner", "package#aaa01.tenant", + "package#aaa02.admin", "package#aaa02.owner", "package#aaa02.tenant", + "customer#aab.admin", "customer#aab.owner", "customer#aab.tenant", + "package#aab00.admin", "package#aab00.owner", "package#aab00.tenant", + "package#aab01.admin", "package#aab01.owner", "package#aab01.tenant", + "package#aab02.admin", "package#aab02.owner", "package#aab02.tenant", + "customer#aac.admin", "customer#aac.owner", "customer#aac.tenant", + "package#aac00.admin", "package#aac00.owner", "package#aac00.tenant", + "package#aac01.admin", "package#aac01.owner", "package#aac01.tenant", + "package#aac02.admin", "package#aac02.owner", "package#aac02.tenant" + // @formatter:on + }; + + @Test + public void hostsharingAdmin_withoutAssumedRole_canViewAllRbacRoles() { + // given + currentUser("mike@hostsharing.net"); + + // when + final var result = rbacRoleRepository.findAll(); + + // then + exactlyTheseRbacRolesAreReturned(result, ALL_TEST_DATA_ROLES); + } + + @Test + public void hostsharingAdmin_withAssumedHostsharingAdminRole_canViewAllRbacRoles() { + given: + currentUser("mike@hostsharing.net"); + assumedRoles("global#hostsharing.admin"); + + // when + final var result = rbacRoleRepository.findAll(); + + then: + exactlyTheseRbacRolesAreReturned(result, ALL_TEST_DATA_ROLES); + } + + @Test + public void RbacRoleAdmin_withoutAssumedRole_canViewOnlyItsOwnRbacRole() { + // given: + currentUser("admin@aaa.example.com"); + + // when: + final var result = rbacRoleRepository.findAll(); + + // then: + exactlyTheseRbacRolesAreReturned( + result, + // @formatter:off + "customer#aaa.admin", "customer#aaa.tenant", + "package#aaa00.admin", "package#aaa00.owner", "package#aaa00.tenant", + "package#aaa01.admin", "package#aaa01.owner", "package#aaa01.tenant", + "package#aaa02.admin", "package#aaa02.owner", "package#aaa02.tenant" + // @formatter:on + ); + } + + @Test + public void RbacRoleAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyItsOwnRbacRole() { + currentUser("admin@aaa.example.com"); + assumedRoles("package#aaa00.admin"); + + final var result = rbacRoleRepository.findAll(); + + exactlyTheseRbacRolesAreReturned(result, "customer#aaa.tenant", "package#aaa00.tenant", "package#aaa00.admin"); + } + + @Test + public void RbacRoleAdmin_withAssumedAlienPackageAdminRole_cannotViewAnyRbacRole() { + // given: + currentUser("admin@aaa.example.com"); + assumedRoles("package#aab00.admin"); + + // when + final var attempt = attempt( + em, + () -> rbacRoleRepository.findAll()); + + // then + attempt.assertExceptionWithRootCauseMessage( + JpaSystemException.class, + "user admin@aaa.example.com .* has no permission to assume role package#aab00#admin"); + } + + @Test + void unknownUser_withoutAssumedRole_cannotViewAnyRbacRoles() { + currentUser("unknown@example.org"); + + final var attempt = attempt( + em, + () -> rbacRoleRepository.findAll()); + + attempt.assertExceptionWithRootCauseMessage( + JpaSystemException.class, + "hsadminng.currentUser defined as unknown@example.org, but does not exists"); + } + + @Test + @Transactional + void unknownUser_withAssumedRbacRoleRole_cannotViewAnyRbacRoles() { + currentUser("unknown@example.org"); + assumedRoles("RbacRole#aaa.admin"); + + final var attempt = attempt( + em, + () -> rbacRoleRepository.findAll()); + + attempt.assertExceptionWithRootCauseMessage( + JpaSystemException.class, + "hsadminng.currentUser defined as unknown@example.org, but does not exists"); + } + + } + + void currentUser(final String currentUser) { + context.setCurrentUser(currentUser); + assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser); + } + + void assumedRoles(final String assumedRoles) { + context.assumeRoles(assumedRoles); + assertThat(context.getAssumedRoles()).as("precondition").containsExactly(assumedRoles.split(";")); + } + + void exactlyTheseRbacRolesAreReturned(final Iterable actualResult, final String... rbacRoleNames) { + assertThat(actualResult) + //.hasSize(rbacRoleNames.length) + .extracting(RbacRoleEntity::getRoleName) + .containsExactlyInAnyOrder(rbacRoleNames); + } + +} diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java new file mode 100644 index 00000000..cabb96b3 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java @@ -0,0 +1,14 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +import static java.util.UUID.randomUUID; + +public class TestRbacRole { + + public static final RbacRoleEntity hostmasterRole = rbacRole("global", "hostsharing", RbacRoleType.admin); + static final RbacRoleEntity customerXxxOwner = rbacRole("customer", "xxx", RbacRoleType.owner); + static final RbacRoleEntity customerXxxAdmin = rbacRole("customer", "xxx", RbacRoleType.admin); + + static public RbacRoleEntity rbacRole(final String objectTable, final String objectIdName, final RbacRoleType roleType) { + return new RbacRoleEntity(randomUUID(), randomUUID(), objectTable, objectIdName, roleType, objectTable+'#'+objectIdName+'.'+roleType); + } +} From 8605d4c4f16ab33626823d741307fee1ddc25ef8 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 4 Aug 2022 09:26:01 +0200 Subject: [PATCH 51/54] PackageController+Repository with name search option --- .../hs/hscustomer/CustomerController.java | 3 +- .../hs/hscustomer/CustomerRepository.java | 2 +- .../hs/hspackage/PackageController.java | 14 +++------ .../hs/hspackage/PackageRepository.java | 8 +++-- .../CustomerControllerRestTest.java | 4 +-- .../CustomerControllerUnitTest.java | 4 +-- .../CustomerRepositoryIntegrationTest.java | 18 +++++------ .../hspackage/PackageControllerRestTest.java | 31 ++++++++++++++++--- 8 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java index e3b2362a..6088f82d 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java @@ -29,11 +29,10 @@ public class CustomerController { if (assumedRoles != null && !assumedRoles.isBlank()) { context.assumeRoles(assumedRoles); } - return customerRepository.findCustomerByOptionalPrefix(prefix); + return customerRepository.findCustomerByOptionalPrefixLike(prefix); } @PostMapping(value = "/api/customers") - @ResponseStatus @Transactional public CustomerEntity addCustomer( @RequestHeader(value = "current-user") String userName, diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java index 51ed90d9..b28dc8cf 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java @@ -14,7 +14,7 @@ public interface CustomerRepository extends Repository { Optional findByUuid(UUID id); @Query("SELECT c FROM CustomerEntity c WHERE :prefix is null or c.prefix like concat(:prefix, '%')") - List findCustomerByOptionalPrefix(@Param("prefix") String prefix); + List findCustomerByOptionalPrefixLike(@Param("prefix") String prefix); CustomerEntity save(final CustomerEntity entity); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageController.java b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageController.java index 63b071e9..9d29f53c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageController.java @@ -2,16 +2,12 @@ package net.hostsharing.hsadminng.hs.hspackage; import net.hostsharing.hsadminng.context.Context; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import javax.transaction.Transactional; import java.util.List; -@Controller +@RestController public class PackageController { @Autowired @@ -20,18 +16,18 @@ public class PackageController { @Autowired private PackageRepository packageRepository; - @ResponseBody @RequestMapping(value = "/api/packages", method = RequestMethod.GET) @Transactional public List listPackages( @RequestHeader(value = "current-user") String userName, - @RequestHeader(value = "assumed-roles", required = false) String assumedRoles + @RequestHeader(value = "assumed-roles", required = false) String assumedRoles, + @RequestParam(required = false) String name ) { context.setCurrentUser(userName); if (assumedRoles != null && !assumedRoles.isBlank()) { context.assumeRoles(assumedRoles); } - return packageRepository.findAll(); + return packageRepository.findAllByOptionalNameLike(name); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageRepository.java index 125bd63a..bafe0882 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hspackage/PackageRepository.java @@ -1,9 +1,13 @@ package net.hostsharing.hsadminng.hs.hspackage; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.Repository; +import java.util.List; import java.util.UUID; -public interface PackageRepository extends JpaRepository { +public interface PackageRepository extends Repository { + @Query("SELECT p FROM PackageEntity p WHERE :name is null or p.name like concat(:name, '%')") + List findAllByOptionalNameLike(final String name); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java index c646d389..7bb58746 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java @@ -30,7 +30,7 @@ class CustomerControllerRestTest { void apiCustomersWillReturnAllCustomersFromRepositoryIfNoCriteriaGiven() throws Exception { // given - when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); + when(customerRepositoryMock.findCustomerByOptionalPrefixLike(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); // when mockMvc.perform(MockMvcRequestBuilders @@ -49,7 +49,7 @@ class CustomerControllerRestTest { void apiCustomersWillReturnMatchingCustomersFromRepositoryIfCriteriaGiven() throws Exception { // given - when(customerRepositoryMock.findCustomerByOptionalPrefix("x")).thenReturn(asList(TestCustomer.xxx)); + when(customerRepositoryMock.findCustomerByOptionalPrefixLike("x")).thenReturn(asList(TestCustomer.xxx)); // when mockMvc.perform(MockMvcRequestBuilders diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java index 30541c50..3a5af0c7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java @@ -27,7 +27,7 @@ class CustomerControllerUnitTest { void apiCustomersWillReturnCustomersFromRepository() throws Exception { // given - when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); + when(customerRepositoryMock.findCustomerByOptionalPrefixLike(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); // when final var pacs = customerController.listCustomers("mike@hostsharing.net", null, null); @@ -42,7 +42,7 @@ class CustomerControllerUnitTest { void findAllWithAssumedCustomerAdminRole() throws Exception { // given - when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(singletonList(TestCustomer.yyy)); + when(customerRepositoryMock.findCustomerByOptionalPrefixLike(null)).thenReturn(singletonList(TestCustomer.yyy)); // when final var pacs = customerController.listCustomers("mike@hostsharing.net", "customer#yyy.admin", null); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java index 7c510b0d..75691819 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java @@ -104,7 +104,7 @@ class CustomerRepositoryIntegrationTest { currentUser("mike@hostsharing.net"); // when - final var result = customerRepository.findCustomerByOptionalPrefix(null); + final var result = customerRepository.findCustomerByOptionalPrefixLike(null); // then exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); @@ -117,7 +117,7 @@ class CustomerRepositoryIntegrationTest { assumedRoles("global#hostsharing.admin"); // when - final var result = customerRepository.findCustomerByOptionalPrefix(null); + final var result = customerRepository.findCustomerByOptionalPrefixLike(null); then: exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); @@ -129,7 +129,7 @@ class CustomerRepositoryIntegrationTest { currentUser("admin@aaa.example.com"); // when: - final var result = customerRepository.findCustomerByOptionalPrefix(null); + final var result = customerRepository.findCustomerByOptionalPrefixLike(null); // then: exactlyTheseCustomersAreReturned(result, "aaa"); @@ -140,7 +140,7 @@ class CustomerRepositoryIntegrationTest { currentUser("admin@aaa.example.com"); assumedRoles("package#aaa00.admin"); - final var result = customerRepository.findCustomerByOptionalPrefix(null); + final var result = customerRepository.findCustomerByOptionalPrefixLike(null); exactlyTheseCustomersAreReturned(result, "aaa"); } @@ -154,7 +154,7 @@ class CustomerRepositoryIntegrationTest { // when final var attempt = attempt( em, - () -> customerRepository.findCustomerByOptionalPrefix(null)); + () -> customerRepository.findCustomerByOptionalPrefixLike(null)); // then attempt.assertExceptionWithRootCauseMessage( @@ -168,7 +168,7 @@ class CustomerRepositoryIntegrationTest { final var attempt = attempt( em, - () -> customerRepository.findCustomerByOptionalPrefix(null)); + () -> customerRepository.findCustomerByOptionalPrefixLike(null)); attempt.assertExceptionWithRootCauseMessage( JpaSystemException.class, @@ -183,7 +183,7 @@ class CustomerRepositoryIntegrationTest { final var attempt = attempt( em, - () -> customerRepository.findCustomerByOptionalPrefix(null)); + () -> customerRepository.findCustomerByOptionalPrefixLike(null)); attempt.assertExceptionWithRootCauseMessage( JpaSystemException.class, @@ -201,7 +201,7 @@ class CustomerRepositoryIntegrationTest { currentUser("mike@hostsharing.net"); // when - final var result = customerRepository.findCustomerByOptionalPrefix("aab"); + final var result = customerRepository.findCustomerByOptionalPrefixLike("aab"); // then exactlyTheseCustomersAreReturned(result, "aab"); @@ -213,7 +213,7 @@ class CustomerRepositoryIntegrationTest { currentUser("admin@aaa.example.com"); // when: - final var result = customerRepository.findCustomerByOptionalPrefix("aab"); + final var result = customerRepository.findCustomerByOptionalPrefixLike("aab"); // then: exactlyTheseCustomersAreReturned(result); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hspackage/PackageControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hspackage/PackageControllerRestTest.java index bc7691f1..92ebd1ef 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hspackage/PackageControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hspackage/PackageControllerRestTest.java @@ -9,7 +9,8 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static java.util.Arrays.asList; +import java.util.List; + import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.verify; @@ -28,11 +29,11 @@ class PackageControllerRestTest { PackageRepository packageRepositoryMock; @Test - void findAll() throws Exception { + void listPackagesWithoutNameParameter() throws Exception { // given - final var givenPacs = asList(TestPackage.xxx00, TestPackage.xxx01, TestPackage.xxx02); - when(packageRepositoryMock.findAll()).thenReturn(givenPacs); + final var givenPacs = List.of(TestPackage.xxx00, TestPackage.xxx01, TestPackage.xxx02); + when(packageRepositoryMock.findAllByOptionalNameLike(null)).thenReturn(givenPacs); // when final var pacs = mockMvc.perform(MockMvcRequestBuilders @@ -52,4 +53,26 @@ class PackageControllerRestTest { verify(contextMock).assumeRoles("customer#xxx.admin"); } + @Test + void listPackagesWithNameParameter() throws Exception { + + // given + final var givenPacs = List.of(TestPackage.xxx01); + when(packageRepositoryMock.findAllByOptionalNameLike("xxx01")).thenReturn(givenPacs); + + // when + final var pacs = mockMvc.perform(MockMvcRequestBuilders + .get("/api/packages?name=xxx01") + .header("current-user", "mike@hostsharing.net") + .header("assumed-roles", "customer#xxx.admin") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0].name", is("xxx01"))); + + verify(contextMock).setCurrentUser("mike@hostsharing.net"); + verify(contextMock).assumeRoles("customer#xxx.admin"); + } } From a2f2fdb6a4b6e0b82fb648733e732bdb65e35873 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 4 Aug 2022 09:41:27 +0200 Subject: [PATCH 52/54] add spotless --- README.md | 48 +++++++++++++++++++ TODO.md | 10 ++-- build.gradle | 13 ++++- .../rbac/rbacrole/RbacRoleRepository.java | 1 - .../hsadminng/hs/hscustomer/TestCustomer.java | 1 - .../hsadminng/hs/hspackage/TestPackage.java | 1 - 6 files changed, 65 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 319988be..7b471a5c 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,54 @@ You can explore the prototype as follows: - then run `historization.sql` in the database, - finally run `examples.sql` in the database. +## Coding Guidelines + +### Directory and Package Structure + +Generally, the standard Java directory structure is used, where productive and test code are sparated like this: + +``` +src + main/ + java/ + net.hostsharing.hasadminng/ + resources/ + + test/ + java/ + net.hostsharing.hasadminng/ + resources/ +``` + +The Java package structure below contains: + +- config and global (utility) packages, + these should not access any other packages within the project +- rbac, containing all packages related to the RBAC subsystem +- hs, containing Hostsharing business object related packages + +Underneath of rbac and hs, the structure is business oriented, NOT technical / layer -oriented. + +Some of these rules are checked with *ArchUnit* unit tests. + +### Spotless Code Formatting + +Code formatting for Java is checked via *spotless*. +The formatting style can be checked with this command: + +```shell +gw spotlessCheck +``` + +This task is also included in `gw build`. + +To apply formatting rules, use: + +```shell +gw spotlessApply +``` + + ## How To ### How to Use a Persistent Database for Integration Tests? diff --git a/TODO.md b/TODO.md index 96c2a7b7..0670559d 100644 --- a/TODO.md +++ b/TODO.md @@ -23,13 +23,13 @@ This document is just for tracking the initial development project and thus only | ID | Beschreibung | Budget | Aufwand | Leistung | |:----|:-----------------------------------------------------------|---------:|--------:|---------:| -| DEV | Aufbau der Entwicklungsumgebung (bis inkl. Unit-Tests) | 16 | | | -| ATN | Entwurf des Authorisierungs-Systems | 40 | 100 | 36 | +| DEV | Aufbau der Entwicklungsumgebung (bis inkl. Unit-Tests) | 16 | 12 | 16 | +| ATN | Entwurf des Authorisierungs-Systems | 40 | 68 | 36 | | ATZ | Auswahl und Implementierung des Authentifizierungs-Systems | 20 | | | -| ITS | Aufbau einer Umgebung für Integrationstests (*1) | 4 | 4 | 20 | -| ATS | Aufbau einer Umgebung für Akzeptanzteste (*1) | 16 | 3 | 4 | +| ITS | Aufbau einer Umgebung für Integrationstests (*1) | 4 | 4 | 4 | +| ATS | Aufbau einer Umgebung für Akzeptanztests (*1) | 16 | 3 | | | PIP | Aufbau einer Build- und Testpipeline | 20 | | | -| ARC | Aufbau einer Architekturkontrolle | 8 | | | +| ARC | Aufbau einer Architekturkontrolle | 8 | 2 | 2 | | | | | | | (*1: ITS+ATS sind aufgesplittet aus TST mit 20 geplanten Stunden entstanden) diff --git a/build.gradle b/build.gradle index 575110be..1554c337 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,8 @@ plugins { + id 'java' id 'org.springframework.boot' version '2.7.2' id 'io.spring.dependency-management' version '1.0.12.RELEASE' - id 'java' + id "com.diffplug.spotless" version "6.9.0" } group = 'net.hostsharing' @@ -22,6 +23,8 @@ ext { set('testcontainersVersion', "1.17.3") } +// wrapper + dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-rest' @@ -55,3 +58,11 @@ dependencyManagement { tasks.named('test') { useJUnitPlatform() } + +spotless { + java { + removeUnusedImports() + endWithNewline() + toggleOffOn() + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java index 0af5091c..abe6738c 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepository.java @@ -14,4 +14,3 @@ public interface RbacRoleRepository extends Repository { */ List findAll(); } - diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/TestCustomer.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/TestCustomer.java index c77b0f91..c92cad82 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/TestCustomer.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/TestCustomer.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.hscustomer; -import net.hostsharing.hsadminng.hs.hscustomer.CustomerEntity; import static java.util.UUID.randomUUID; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hspackage/TestPackage.java b/src/test/java/net/hostsharing/hsadminng/hs/hspackage/TestPackage.java index e586cc1c..f2fde56f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hspackage/TestPackage.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hspackage/TestPackage.java @@ -1,7 +1,6 @@ package net.hostsharing.hsadminng.hs.hspackage; import net.hostsharing.hsadminng.hs.hscustomer.CustomerEntity; -import net.hostsharing.hsadminng.hs.hspackage.PackageEntity; import net.hostsharing.hsadminng.hs.hscustomer.TestCustomer; import static java.util.UUID.randomUUID; From 2eb8b70517f2c80fd9a4f36391d54f25678679f2 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 4 Aug 2022 10:35:28 +0200 Subject: [PATCH 53/54] add glossary --- README.md | 6 +++++ doc/glossary.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 doc/glossary.md diff --git a/README.md b/README.md index 7b471a5c..5830fdfa 100644 --- a/README.md +++ b/README.md @@ -324,3 +324,9 @@ gw bootRun **⚠** Just don't forget switching to the migration mode, once there is a production database! + +## Further Documentation + +- the `doc` directory contains architecture concepts and a glossary +- TODO.md tracks requirements and progress for the contract of the initial project, + please do not amend anything in this document diff --git a/doc/glossary.md b/doc/glossary.md new file mode 100644 index 00000000..cef96856 --- /dev/null +++ b/doc/glossary.md @@ -0,0 +1,64 @@ +### hsadminNg Glossary + +This is a collection of terms used in this project, which either might not be generally known or unclear in meaning. +If you miss something, please add it with a `TODO` marker. + +#### Blackbox-Test + +A blackbox-test does not know and not consider such internals of an implementation, it just tests externally observable behaviour. + + +#### Business Object + +Used in the RBAC-system to refer to an object from the business realm. +The usual term is *domain object* but in our context, the term *domain* could be too easily confused with a DNS *Internet domain*. + + +#### Dummy + +A *dummy* is a kind of *Test-Double* which replaces a real dependency which is not really needed in the test case. + + +#### Fake + +A *fake* is a kind of *Test-Double* without using any library, but rather a manual fake implementation of a dependency. + + +#### Mock + +A *mock* is a kind of *Test-Double* which can be configured to behaviours as needed by a test-case. + +Often the term "mock" is used in a generic way, because typical mocking libraries like *Mockito* can also be used as dummies or spies and can replace fakes. + + +#### RBAC + +Abbreviation for *Role Based Access Control*. +A system to control access to business objects by defining users, roles, and permissions. +See also [The INCITS 359-2012 Standard](https://www.techstreet.com/standards/incits-359-2012?product_id=1837530). + +In our case we are implementing a hierarchical RBAC for a hierarchical and dynamic business object structure. +More information can be found in our [RBAC Architecture Document](rbac.md). + + +#### Tenant + +*Tenant* is one of the standard roles of Hostsharing's RBAC system. +It is assigned as a sub-role to those who have rights on sub-objects of a business object. +Usually, tenants can only view the contents. + +Generally, tenant roles only apply for the mere existence, id and name of a business object, +not for internal details. +E.g. a tenant of a customer could be the administrator of a hosting package of that customer. +They can view some identifying information of that customer, but not view their billing and banking information. + + +#### Whitebox-Test + +A whitebox-test knows and considers the internals of an implementation, e.g. it knows which dependencies it needs and can test special, implementation-dependent cases. + + +#### Test-Double + +A "double" is a general term for something which replaces a real implementation of a dependency of the unit under test. +This can be a "dummy", a "fake", a "mock", a "spy" or a "stub". From bf3da34d3251615218ed76cf27855c72649390b2 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 4 Aug 2022 10:51:21 +0200 Subject: [PATCH 54/54] add adr for row-level-security --- ...2022-07-18.row-level-security-mechanism.md | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 doc/adr/2022-07-18.row-level-security-mechanism.md diff --git a/doc/adr/2022-07-18.row-level-security-mechanism.md b/doc/adr/2022-07-18.row-level-security-mechanism.md new file mode 100644 index 00000000..51b72245 --- /dev/null +++ b/doc/adr/2022-07-18.row-level-security-mechanism.md @@ -0,0 +1,160 @@ +# Use VIEWs with JOIN into Permission-Assignments for Row-Level-Security + +**Status:** +- [x] proposed by Michael Hönnig +- [ ] accepted by (Participants) +- [ ] rejected by (Participants) +- [ ] superseded by (superseding ADR) + +## Context and Problem Statement + +We need to decide how to apply the access rules defined in our RBAC system to the visibility of table rows for the accessing user. + +The core problem here is, that in our RBAC system, determining the permissions of the accessing user has to consider a hierarchy of roles. + +### Technical Background + +The session variable `hsadminng.currentUser` contains the accessing (domain-level) user, which is unrelated to the PostgreSQL user). + +Given is a stored function `isPermissionGrantedToSubject` which detects if the accessing user has a given permission (e.g. 'view'). + +Given is also a stored function `queryAllPermissionsOfSubjectId` which returns the flattened view to all permissions assigned to the given accessing user. + +In the following code snippets `customer` is just an example domain table. + +## Considered Options + +* Perform Visibility-Checks programmatically in the Backend +* Add Visibility-Checks in the Backend +* POLICY with ENABLE ROW LEVEL SECURITY +* VIEW-RULE with ON SELECT DO INSTEAD +* VIEW with JOIN into Flattened Permissions + +### Perform Visibility-Checks programmatically in the Backend + +In this solution, the database ignores row level visibility and returns all rows which match a given query. Afterwards, the result is filtered programmatically with Java-code in the backend. + +#### Advantages + +Very flexible access, programmatic, rules could be implemented. + +The role-hierarchy and permissions for currently logged-in users user could be cached in the backend. + +The access logic can be tested in pure Java unit tests. + +At least regarding this aspect, an in-memory database could be used for integration testing; though the recursive Role-evaluation uses PostgreSQL features anyway. + +#### Disadvantages + +It's inefficient when initial query is not very restrictive, e.g. as on overview pages in a frontend, which often show all accessible objects, large parts or even whole database tables need to be transferred from the database to the backend. + +It's error-prone and security leaks can happen too easily, because after every query the access rights for all participating joins have to be considered. + +### Add Visibility-Checks in the Backend + +In this solution again, the database ignores row level visibility and returns all rows which match a given query. And the backend adds filter conditions to each query sent to the database. + +#### Advantages + +At least regarding this aspect, an in-memory database could be used for integration testing. + +#### Disadvantages + +It's error-prone and security leaks can happen too easily, because for every query the access rights for all participating joins have to be considered. + +### POLICY with ENABLE ROW LEVEL SECURITY + +For restricted DB-users, which are used by the backend, access to rows is filtered using a policy: + + SET SESSION AUTHORIZATION DEFAULT; + CREATE ROLE restricted; + GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO restricted; + ALTER TABLE customer ENABLE ROW LEVEL SECURITY; + CREATE POLICY customer_policy ON customer + FOR SELECT + TO restricted + USING ( + isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserId()) + ); + + SET SESSION AUTHORIZATION restricted; + SET hsadminng.currentUser TO 'alex@example.com'; + SELECT * from customer; -- will only return visible rows + +#### Advantages + +Using POLICY together with ENABLE ROW LEVEL SECURITY is the PostgreSQL native mechanism to control access to data on the role level. Therefore, it looked like an obvious and elegant solution. + +Every access at from the backend is under access control at the database level. + +### Disadvantages + +Unfortunately security mechanisms in PostgreSQL prevent the query optimizer to work well beyond ownership barriers (session user vs. table owner) and a SELECT from a table with 1 million objects needed over 30 seconds with our hierarchical RBAC policy. + +We are bound to PostgreSQL, including integration tests and testing the RBAC system itself. + +### VIEW-RULE with ON SELECT DO INSTEAD + + SET SESSION SESSION AUTHORIZATION DEFAULT; + CREATE VIEW cust_view AS + SELECT * FROM customer; + CREATE OR REPLACE RULE "_RETURN" AS + ON SELECT TO cust_view + DO INSTEAD + SELECT * FROM customer WHERE isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserId()); + + SET SESSION AUTHORIZATION restricted; + SET hsadminng.currentUser TO 'alex@example.com'; + SELECT * from customer; -- will only return visible rows + +#### Advantages + +Every access at from the backend is under access control at the database level. + +Also using ON UPDATE etc., original tables could be completely hidden from the backend, and thus improved security. + +### Disadvantages + +Unfortunately security mechanisms in PostgreSQL prevent the query optimizer to work well beyond ownership barriers (session user vs. table owner) and a SELECT from a table with 1 million objects needed over 30 seconds with our hierarchical RBAC policy. + +We are bound to PostgreSQL, including integration tests and testing the RBAC system itself. + +An extra view needed for every table. + + +### VIEW with JOIN into flattened permissions + +We do not access the tables directly from the backend, but via views which join the flattened permissions + + SET SESSION SESSION AUTHORIZATION DEFAULT; + CREATE OR REPLACE VIEW cust_view AS + SELECT c.id, c.reference, c.prefix + FROM customer AS c + JOIN queryAllPermissionsOfSubjectId(currentUserId()) AS p + ON p.tableName='customer' AND p.rowId=c.id AND p.op='view'; + GRANT ALL PRIVILEGES ON cust_view TO restricted; + + SET SESSION SESSION AUTHORIZATION restricted; + SET hsadminng.currentUser TO 'alex@example.com'; + SELECT * from cust_view; -- will only return visible rows + +Alternatively the JOIN could also be applied in a "ON SELECT DO INSTEAD"-RULE, if there is any advantage for later features. + +#### Advantages + +Every access at from the backend is under access control at the database level. + +No special PostgreSQL features needed; though the recursive Role-evaluation uses PostgreSQL features anyway. + +Very fast, on my laptop a SELECT * FROM a table with 1 million rows just took about 50ms. + +Also using ON UPDATE etc., original tables could be completely hidden from the backend, and thus improved security. + +### Disadvantages + +An extra view needed for every table. + + +## Decision Outcome + +We chose the option **"VIEW with JOIN into flattened permissions"** because it supports the best combination of performance and security with almost no disadvantge.