From b54df0ddd0c6a0372327c5aa3668e5a6458fcd64 Mon Sep 17 00:00:00 2001 From: st782s Date: Thu, 4 May 2017 07:48:42 -0400 Subject: [PORTAL-7] Rebase This rebasing includes common libraries and common overlays projects abstraction of components Change-Id: I9a24a338665c7cd058978e8636bc412d9e2fdce8 Signed-off-by: st782s --- ecomp-portal-BE-common/.gitignore | 5 + ecomp-portal-BE-common/license/licenses.properties | 1 + .../license/my_license/header.txt | 11 + .../license/my_license/license.txt | 4 + ecomp-portal-BE-common/pom.xml | 666 + .../src/main/java/jarutil/ExtractJar.java | 204 + .../openecomp/portalapp/command/EPLoginBean.java | 186 + .../portalapp/command/PostSearchBean.java | 373 + .../openecomp/portalapp/config/SwaggerConfig.java | 58 + .../portalapp/config/SwaggerMVCConfig.java | 37 + .../controller/EPFusionBaseController.java | 106 + .../controller/EPRestrictedBaseController.java | 52 + .../EPRestrictedRESTfulBaseController.java | 54 + .../controller/EPUnRestrictedBaseController.java | 42 + .../sessionmgt/SessionCommunicationController.java | 72 + .../controller/RolesApprovalSystemController.java | 143 + .../model/ExternalSystemRoleApproval.java | 41 + .../model/ExternalSystemUser.java | 74 + .../portalapp/lm/FusionLicenseManagerImpl.java | 88 + .../portalapp/lm/LicenseableClassImpl.java | 62 + .../java/org/openecomp/portalapp/model/Result.java | 37 + .../portal/controller/AppCatalogController.java | 124 + .../portal/controller/AppContactUsController.java | 224 + .../portal/controller/AppsController.java | 670 + .../controller/AppsControllerExternalRequest.java | 374 + .../portal/controller/AuditLogController.java | 106 + .../controller/BEPropertyReaderController.java | 46 + .../controller/BasicAuthAccountController.java | 141 + .../controller/BasicAuthenticationController.java | 28 + .../portal/controller/CommonWidgetController.java | 79 + .../portal/controller/ConsulClientController.java | 90 + .../portal/controller/DashboardController.java | 385 + .../DashboardSearchResultController.java | 243 + .../controller/ExternalAppsRestfulController.java | 206 + .../controller/FunctionalMenuController.java | 555 + .../portal/controller/GetAccessController.java | 73 + .../portal/controller/HealthCheckController.java | 235 + .../portal/controller/ManifestController.java | 90 + .../portal/controller/MicroserviceController.java | 141 + .../controller/MicroserviceProxyController.java | 108 + .../portal/controller/PortalAdminController.java | 200 + .../portal/controller/RoleManageController.java | 166 + .../controller/SharedContextRestController.java | 343 + .../portal/controller/TicketEventController.java | 179 + .../controller/UserNotificationController.java | 216 + .../portal/controller/UserRolesController.java | 435 + .../controller/WebAnalyticsExtAppController.java | 157 + .../controller/WidgetsCatalogController.java | 376 + .../controller/WidgetsCatalogMarkupController.java | 83 + .../portalapp/portal/domain/AdminUserApp.java | 88 + .../portal/domain/AdminUserApplications.java | 96 + .../org/openecomp/portalapp/portal/domain/App.java | 62 + .../portalapp/portal/domain/AppContactUs.java | 86 + .../portal/domain/AppIdAndNameTransportModel.java | 63 + .../portalapp/portal/domain/AppsResponse.java | 76 + .../portalapp/portal/domain/BEProperty.java | 43 + .../portal/domain/BasicAuthCredentials.java | 95 + .../openecomp/portalapp/portal/domain/EPApp.java | 292 + .../portalapp/portal/domain/EPEndpoint.java | 37 + .../portalapp/portal/domain/EPEndpointAccount.java | 46 + .../openecomp/portalapp/portal/domain/EPRole.java | 202 + .../openecomp/portalapp/portal/domain/EPUser.java | 650 + .../portalapp/portal/domain/EPUserApp.java | 119 + .../portal/domain/EPUserAppCatalogRoles.java | 92 + .../portal/domain/EPUserAppRolesRequest.java | 87 + .../portal/domain/EPUserAppRolesRequestDetail.java | 52 + .../domain/EPUserAppsManualSortPreference.java | 50 + .../portal/domain/EPUserAppsSortPreference.java | 44 + .../portal/domain/EPUserNotification.java | 57 + .../domain/EPWidgetsManualSortPreference.java | 70 + .../portalapp/portal/domain/EcompApp.java | 146 + .../portalapp/portal/domain/EcompAppRole.java | 80 + .../portalapp/portal/domain/EcompAppRoleId.java | 83 + .../portalapp/portal/domain/EcompAuditLog.java | 61 + .../portalapp/portal/domain/EpUserAppRoles.java | 63 + .../portal/domain/ExternalSystemAccess.java | 49 + .../portal/domain/FunctionalMenuItemWithAppID.java | 96 + .../portalapp/portal/domain/GetAccessResult.java | 114 + .../portalapp/portal/domain/GetAccessResultId.java | 64 + .../portalapp/portal/domain/MicroserviceData.java | 146 + .../portal/domain/MicroserviceParameter.java | 82 + .../portal/domain/PersUserAppSelection.java | 78 + .../portal/domain/PersUserWidgetSelection.java | 81 + .../openecomp/portalapp/portal/domain/RoleApp.java | 114 + .../portalapp/portal/domain/SharedContext.java | 161 + .../portalapp/portal/domain/UserIdRoleId.java | 77 + .../portalapp/portal/domain/UserRole.java | 91 + .../portalapp/portal/domain/UserRoles.java | 113 + .../openecomp/portalapp/portal/domain/Widget.java | 105 + .../portalapp/portal/domain/WidgetCatalog.java | 109 + .../portal/domain/WidgetCatalogParameter.java | 90 + .../portal/domain/WidgetParameterResult.java | 70 + .../portal/domain/WidgetServiceHeaders.java | 54 + .../portal/ecomp/model/AppCatalogItem.java | 249 + .../ecomp/model/AppCategoryFunctionsItem.java | 140 + .../portal/ecomp/model/AppContactUsItem.java | 171 + .../portal/ecomp/model/CommonWidgetsEnum.java | 36 + .../portal/ecomp/model/PortalRestResponse.java | 101 + .../portal/ecomp/model/PortalRestStatusEnum.java | 36 + .../portal/ecomp/model/SearchResultItem.java | 86 + .../exceptions/NoHealthyServiceException.java | 36 + .../interceptor/PortalResourceInterceptor.java | 335 + .../portalapp/portal/listener/HealthMonitor.java | 355 + .../portal/listener/UserContextListener.java | 51 + .../portal/listener/UserSessionListener.java | 126 + .../portalapp/portal/logging/aop/EPAuditLog.java | 31 + .../portal/logging/aop/EPEELFLoggerAdvice.java | 371 + .../portalapp/portal/logging/aop/EPMetricsLog.java | 31 + .../logging/format/ApplicationCodes.properties | 141 + .../portal/logging/format/EPAppMessagesEnum.java | 260 + .../portal/logging/format/EPErrorCodesEnum.java | 95 + .../portalapp/portal/logging/logic/EPLogUtil.java | 270 + .../portal/service/AdminRolesService.java | 56 + .../portal/service/AdminRolesServiceImpl.java | 329 + .../portal/service/AppContactUsService.java | 55 + .../portal/service/AppContactUsServiceImpl.java | 189 + .../service/ApplicationsRestClientService.java | 53 + .../service/ApplicationsRestClientServiceImpl.java | 411 + .../portalapp/portal/service/AppsCacheService.java | 42 + .../portal/service/AppsCacheServiceImple.java | 136 + .../portal/service/BasicAuthAccountService.java | 47 + .../service/BasicAuthAccountServiceImpl.java | 205 + .../BasicAuthenticationCredentialService.java | 35 + .../BasicAuthenticationCredentialServiceImpl.java | 81 + .../portal/service/ConsulHealthService.java | 32 + .../portal/service/ConsulHealthServiceImpl.java | 91 + .../portal/service/DashboardSearchService.java | 37 + .../portal/service/DashboardSearchServiceImpl.java | 100 + .../portal/service/EPAppCommonServiceImpl.java | 1368 + .../portalapp/portal/service/EPAppService.java | 213 + .../portalapp/portal/service/EPAuditService.java | 27 + .../portal/service/EPAuditServiceImpl.java | 96 + .../portalapp/portal/service/EPLdapService.java | 31 + .../portal/service/EPLdapServiceImpl.java | 288 + .../portal/service/EPLeftMenuService.java | 44 + .../portal/service/EPLeftMenuServiceImpl.java | 195 + .../portalapp/portal/service/EPLoginService.java | 70 + .../portal/service/EPLoginServiceImpl.java | 234 + .../portalapp/portal/service/EPRoleService.java | 37 + .../portal/service/EPRoleServiceImpl.java | 171 + .../portal/service/FunctionalMenuService.java | 58 + .../portal/service/FunctionalMenuServiceImpl.java | 963 + .../portalapp/portal/service/GetAccessService.java | 33 + .../portal/service/GetAccessServiceImpl.java | 61 + .../portalapp/portal/service/ManifestService.java | 33 + .../portal/service/ManifestServiceImpl.java | 62 + .../portal/service/MicroserviceProxyService.java | 59 + .../service/MicroserviceProxyServiceImpl.java | 222 + .../portal/service/MicroserviceService.java | 86 + .../portal/service/MicroserviceServiceImpl.java | 212 + .../portal/service/PersUserAppService.java | 42 + .../portal/service/PersUserAppServiceImpl.java | 141 + .../portal/service/PersUserWidgetService.java | 41 + .../portal/service/PersUserWidgetServiceImpl.java | 81 + .../portal/service/PortalAdminService.java | 31 + .../portal/service/PortalAdminServiceImpl.java | 210 + .../portalapp/portal/service/SearchService.java | 30 + .../portal/service/SearchServiceImpl.java | 175 + .../portal/service/SharedContextService.java | 98 + .../portal/service/SharedContextServiceImpl.java | 177 + .../portal/service/UserNotificationService.java | 55 + .../service/UserNotificationServiceImpl.java | 211 + .../portalapp/portal/service/UserRolesService.java | 67 + .../portalapp/portal/service/UserService.java | 34 + .../portalapp/portal/service/UserServiceImpl.java | 260 + .../portal/service/WidgetParameterService.java | 64 + .../portal/service/WidgetParameterServiceImpl.java | 92 + .../portalapp/portal/service/WidgetService.java | 36 + .../portal/service/WidgetServiceImpl.java | 276 + .../portal/transport/AllAppsWithRolesForUser.java | 40 + .../portalapp/portal/transport/Analytics.java | 66 + .../transport/AppCatalogPersonalization.java | 94 + .../portal/transport/AppNameIdIsAdmin.java | 114 + .../portal/transport/AppWithRolesForUser.java | 73 + .../portal/transport/AppsListWithAdminRole.java | 86 + .../transport/BusinessCardApplicationRole.java | 108 + .../BusinessCardApplicationRolesList.java | 86 + .../portalapp/portal/transport/CommonWidget.java | 139 + .../portal/transport/CommonWidgetMeta.java | 50 + .../portal/transport/EPAppsManualPreference.java | 114 + .../portal/transport/EPAppsSortPreference.java | 47 + .../transport/EPDeleteAppsManualSortPref.java | 51 + .../portal/transport/EPWidgetsSortPreference.java | 92 + .../portal/transport/EpNotificationItem.java | 328 + .../portal/transport/EpNotificationItemExtVO.java | 119 + .../portal/transport/EpNotificationItemVO.java | 191 + .../portal/transport/EpRoleNotificationItem.java | 90 + .../portalapp/portal/transport/EventWidget.java | 138 + .../portal/transport/EventWidgetMeta.java | 51 + .../transport/FavoritesFunctionalMenuItem.java | 45 + .../transport/FavoritesFunctionalMenuItemJson.java | 57 + .../portal/transport/FieldsValidator.java | 125 + .../portal/transport/FunctionalMenuItem.java | 100 + .../transport/FunctionalMenuItemWithRoles.java | 60 + .../portal/transport/FunctionalMenuRole.java | 115 + .../portalapp/portal/transport/LocalRole.java | 42 + .../portalapp/portal/transport/OnboardingApp.java | 99 + .../portal/transport/OnboardingWidget.java | 65 + .../portalapp/portal/transport/PortalAdmin.java | 53 + .../portal/transport/PortalAdminUserRole.java | 48 + .../portalapp/portal/transport/RemoteRole.java | 44 + .../portal/transport/RemoteUserWithRoles.java | 197 + .../portal/transport/RoleInAppForUser.java | 105 + .../portal/transport/RolesInAppForUser.java | 33 + .../portal/transport/UserApplicationRoles.java | 86 + .../portal/transport/UserWithNameSurnameTitle.java | 39 + .../transport/WidgetCatalogPersonalization.java | 57 + .../portalapp/portal/ueb/EPUebHelper.java | 217 + .../portalapp/portal/ueb/EPUebMsgTypes.java | 27 + .../portal/utils/CustomLoggingFilter.java | 54 + .../portal/utils/EPCommonSystemProperties.java | 64 + .../portalapp/portal/utils/EcompPortalUtils.java | 331 + .../portalapp/portal/utils/EcompVersion.java | 33 + .../portalapp/portal/utils/HashMapFromList.java | 99 + .../portalapp/portal/utils/ParallelExecutor.java | 83 + .../portalapp/portal/utils/PortalConstants.java | 26 + .../portalapp/service/AdminAuthExtension.java | 36 + .../portalapp/service/EPProfileService.java | 36 + .../portalapp/service/EPProfileServiceImpl.java | 77 + .../service/RemoteWebServiceCallService.java | 48 + .../service/sessionmgt/CoreTimeoutHandler.java | 166 + .../service/sessionmgt/ManageService.java | 101 + .../service/sessionmgt/SessionCommunication.java | 271 + .../service/sessionmgt/TimeoutHandler.java | 252 + .../org/openecomp/portalapp/util/EPUserUtils.java | 407 + .../portalapp/util/SessionCookieUtil.java | 129 + .../org/openecomp/portalapp/util/SystemType.java | 26 + .../core/lm/FusionLicenseManagerUtils.java | 75 + .../src/main/webapp/META-INF/MANIFEST.MF | 3 + .../src/main/webapp/WEB-INF/defs/definitions.xml | 38 + .../webapp/WEB-INF/fusion/defs/definitions.xml | 203 + .../src/main/webapp/WEB-INF/fusion/jsp/.gitignore | 0 .../src/main/webapp/WEB-INF/fusion/orm/EP.hbm.xml | 1595 + .../main/webapp/WEB-INF/fusion/orm/Fusion.hbm.xml | 335 + .../webapp/WEB-INF/fusion/orm/Workflow.hbm.xml | 48 + .../src/main/webapp/WEB-INF/jsp/error.jsp | 20 + .../src/main/webapp/WEB-INF/jsp/index.jsp | 21 + .../src/main/webapp/WEB-INF/jsp/login.jsp | 120 + .../src/main/webapp/WEB-INF/jsp/login_external.jsp | 162 + .../static/ebz/angular_js/angular-sanitize.js | 647 + .../WEB-INF/static/ebz/angular_js/angular.js | 22024 ++++++++++ .../webapp/WEB-INF/static/ebz/angular_js/app.js | 3 + .../static/ebz/angular_js/att-abs-tpls-1.2.51.js | 17587 ++++++++ .../static/ebz/angular_js/attHeaderSnippet.js | 210 + .../WEB-INF/static/ebz/angular_js/attHeader_new.js | 1375 + .../WEB-INF/static/ebz/angular_js/att_abs_tpls.js | 18514 ++++++++ .../static/ebz/angular_js/checklist-model.js | 99 + .../static/ebz/angular_js/checklist-model.min.js | 1 + .../static/ebz/angular_js/d7100892.modernizr.js | 1 + .../static/ebz/angular_js/ebiz_keepalive.js | 8 + .../webapp/WEB-INF/static/ebz/angular_js/footer.js | 110 + .../WEB-INF/static/ebz/angular_js/footer_new.js | 102 + .../WEB-INF/static/ebz/angular_js/gestures.js | 1495 + .../WEB-INF/static/ebz/angular_js/ng_base.js | 4 + .../static/ebz/angular_js/ui-charts-tpls.js | 3909 ++ .../WEB-INF/static/fusion/images/login_button.gif | Bin 0 -> 1222 bytes .../main/webapp/WEB-INF/static/js/jquery-1.10.2.js | 9789 +++++ .../src/main/webapp/WEB-INF/static/js/jquery-ui.js | 16617 ++++++++ .../webapp/WEB-INF/static/js/jquery.mask.min.js | 12 + .../src/main/webapp/WEB-INF/web.xml | 56 + .../src/main/webapp/images/cache/README.txt | 1 + ecomp-portal-BE-common/src/main/webapp/index.jsp | 34 + .../fusion/external/angular-1.5/angular-animate.js | 4121 ++ .../external/angular-1.5/angular-animate.min.js | 56 + .../fusion/external/angular-1.5/angular-aria.js | 398 + .../external/angular-1.5/angular-aria.min.js | 14 + .../fusion/external/angular-1.5/angular-cookies.js | 322 + .../external/angular-1.5/angular-cookies.min.js | 9 + .../fusion/external/angular-1.5/angular-csp.css | 21 + .../fusion/external/angular-1.5/angular-loader.js | 484 + .../external/angular-1.5/angular-loader.min.js | 10 + .../external/angular-1.5/angular-message-format.js | 980 + .../angular-1.5/angular-message-format.min.js | 26 + .../external/angular-1.5/angular-messages.js | 687 + .../external/angular-1.5/angular-messages.min.js | 12 + .../fusion/external/angular-1.5/angular-mocks.js | 2842 ++ .../external/angular-1.5/angular-resource.js | 768 + .../external/angular-1.5/angular-resource.min.js | 15 + .../fusion/external/angular-1.5/angular-route.js | 1016 + .../external/angular-1.5/angular-route.min.js | 15 + .../external/angular-1.5/angular-sanitize.js | 717 + .../external/angular-1.5/angular-sanitize.min.js | 15 + .../external/angular-1.5/angular-scenario.js | 41849 +++++++++++++++++++ .../fusion/external/angular-1.5/angular-touch.js | 729 + .../external/angular-1.5/angular-touch.min.js | 14 + .../app/fusion/external/angular-1.5/angular.js | 30428 ++++++++++++++ .../app/fusion/external/angular-1.5/angular.min.js | 307 + .../app/fusion/external/angular-1.5/errors.json | 1 + .../app/fusion/external/angular-1.5/version.json | 1 + .../app/fusion/external/angular-1.5/version.txt | 1 + .../angular-ui/ui-bootstrap-tpls-1.1.2.min.js | 10 + .../angular-ui/ui-bootstrap-tpls-1.2.4.min.js | 10 + .../angular-ui/ui-sortable/v0.13.4/sortable.min.js | 8 + .../app/fusion/external/utils/js/browserCheck.js | 5 + .../static/ebz/angular_js/angular-sanitize.js | 647 + .../main/webapp/static/ebz/angular_js/angular.js | 22024 ++++++++++ .../src/main/webapp/static/ebz/angular_js/app.js | 3 + .../webapp/static/ebz/angular_js/ebiz_keepalive.js | 8 + .../main/webapp/static/ebz/angular_js/gestures.js | 1495 + .../webapp/static/fusion/images/ecomp-login.jpg | Bin 0 -> 66384 bytes .../src/main/webapp/static/fusion/images/ecomp.png | Bin 0 -> 107597 bytes .../webapp/static/fusion/images/ecomp_trans.png | Bin 0 -> 109926 bytes .../src/main/webapp/static/js/jquery-1.10.2.js | 9789 +++++ .../src/main/webapp/static/js/jquery-ui.js | 16617 ++++++++ .../src/main/webapp/static/js/jquery.mask.min.js | 12 + .../portal/controller/SharedContextRestClient.java | 281 + .../SharedContextRestControllerTest.java | 127 + .../controller/SharedContextTestProperties.java | 81 + .../controller/shared-context-test.properties | 28 + .../portal/listener/HealthMonitorTest.java | 36 + .../portal/utils/EcompPortalUtilsTest.java | 37 + 311 files changed, 262651 insertions(+) create mode 100644 ecomp-portal-BE-common/.gitignore create mode 100644 ecomp-portal-BE-common/license/licenses.properties create mode 100644 ecomp-portal-BE-common/license/my_license/header.txt create mode 100644 ecomp-portal-BE-common/license/my_license/license.txt create mode 100644 ecomp-portal-BE-common/pom.xml create mode 100644 ecomp-portal-BE-common/src/main/java/jarutil/ExtractJar.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/command/EPLoginBean.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/command/PostSearchBean.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/config/SwaggerConfig.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/config/SwaggerMVCConfig.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPFusionBaseController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPRestrictedBaseController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPRestrictedRESTfulBaseController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPUnRestrictedBaseController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/sessionmgt/SessionCommunicationController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/controller/RolesApprovalSystemController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/model/ExternalSystemRoleApproval.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/model/ExternalSystemUser.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/lm/FusionLicenseManagerImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/lm/LicenseableClassImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/model/Result.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppCatalogController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppContactUsController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppsController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppsControllerExternalRequest.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AuditLogController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BEPropertyReaderController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BasicAuthAccountController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BasicAuthenticationController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/CommonWidgetController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ConsulClientController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/DashboardController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/DashboardSearchResultController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ExternalAppsRestfulController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/FunctionalMenuController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/GetAccessController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/HealthCheckController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ManifestController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/MicroserviceController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/MicroserviceProxyController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/PortalAdminController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/RoleManageController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/SharedContextRestController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/TicketEventController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/UserNotificationController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/UserRolesController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WebAnalyticsExtAppController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WidgetsCatalogController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WidgetsCatalogMarkupController.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AdminUserApp.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AdminUserApplications.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/App.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppContactUs.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppIdAndNameTransportModel.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppsResponse.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/BEProperty.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/BasicAuthCredentials.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPApp.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPEndpoint.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPEndpointAccount.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPRole.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUser.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserApp.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppCatalogRoles.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppRolesRequest.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppRolesRequestDetail.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppsManualSortPreference.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppsSortPreference.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserNotification.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPWidgetsManualSortPreference.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompApp.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAppRole.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAppRoleId.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAuditLog.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EpUserAppRoles.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/ExternalSystemAccess.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/FunctionalMenuItemWithAppID.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/GetAccessResult.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/GetAccessResultId.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/MicroserviceData.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/MicroserviceParameter.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/PersUserAppSelection.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/PersUserWidgetSelection.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/RoleApp.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/SharedContext.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserIdRoleId.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserRole.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserRoles.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/Widget.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetCatalog.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetCatalogParameter.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetParameterResult.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetServiceHeaders.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppCatalogItem.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppCategoryFunctionsItem.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppContactUsItem.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/CommonWidgetsEnum.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/PortalRestResponse.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/PortalRestStatusEnum.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/SearchResultItem.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/exceptions/NoHealthyServiceException.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/interceptor/PortalResourceInterceptor.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/HealthMonitor.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/UserContextListener.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/UserSessionListener.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPAuditLog.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPEELFLoggerAdvice.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPMetricsLog.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/ApplicationCodes.properties create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/EPAppMessagesEnum.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/EPErrorCodesEnum.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/logic/EPLogUtil.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AdminRolesService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AdminRolesServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppContactUsService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppContactUsServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ApplicationsRestClientService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ApplicationsRestClientServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppsCacheService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppsCacheServiceImple.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthAccountService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthAccountServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthenticationCredentialService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthenticationCredentialServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ConsulHealthService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ConsulHealthServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/DashboardSearchService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/DashboardSearchServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAppCommonServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAppService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAuditService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAuditServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLdapService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLdapServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLeftMenuService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLeftMenuServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLoginService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLoginServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPRoleService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPRoleServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/FunctionalMenuService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/FunctionalMenuServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/GetAccessService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/GetAccessServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ManifestService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ManifestServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceProxyService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceProxyServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserAppService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserAppServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserWidgetService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserWidgetServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PortalAdminService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PortalAdminServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SearchService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SearchServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SharedContextService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SharedContextServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserNotificationService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserNotificationServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserRolesService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetParameterService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetParameterServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AllAppsWithRolesForUser.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/Analytics.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppCatalogPersonalization.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppNameIdIsAdmin.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppWithRolesForUser.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppsListWithAdminRole.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/BusinessCardApplicationRole.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/BusinessCardApplicationRolesList.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/CommonWidget.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/CommonWidgetMeta.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPAppsManualPreference.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPAppsSortPreference.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPDeleteAppsManualSortPref.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPWidgetsSortPreference.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItem.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItemExtVO.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItemVO.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpRoleNotificationItem.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EventWidget.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EventWidgetMeta.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FavoritesFunctionalMenuItem.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FavoritesFunctionalMenuItemJson.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FieldsValidator.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuItem.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuItemWithRoles.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuRole.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/LocalRole.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/OnboardingApp.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/OnboardingWidget.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/PortalAdmin.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/PortalAdminUserRole.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RemoteRole.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RemoteUserWithRoles.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RoleInAppForUser.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RolesInAppForUser.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/UserApplicationRoles.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/UserWithNameSurnameTitle.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/WidgetCatalogPersonalization.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ueb/EPUebHelper.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ueb/EPUebMsgTypes.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/CustomLoggingFilter.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EPCommonSystemProperties.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EcompPortalUtils.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EcompVersion.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/HashMapFromList.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/ParallelExecutor.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/PortalConstants.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/AdminAuthExtension.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/EPProfileService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/EPProfileServiceImpl.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/RemoteWebServiceCallService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/CoreTimeoutHandler.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/ManageService.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/SessionCommunication.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/TimeoutHandler.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/EPUserUtils.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/SessionCookieUtil.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/SystemType.java create mode 100644 ecomp-portal-BE-common/src/main/java/org/openecomp/portalsdk/core/lm/FusionLicenseManagerUtils.java create mode 100644 ecomp-portal-BE-common/src/main/webapp/META-INF/MANIFEST.MF create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/defs/definitions.xml create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/defs/definitions.xml create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/jsp/.gitignore create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/EP.hbm.xml create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/Fusion.hbm.xml create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/Workflow.hbm.xml create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/error.jsp create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/index.jsp create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/login.jsp create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/login_external.jsp create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/angular-sanitize.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/angular.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/app.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/att-abs-tpls-1.2.51.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/attHeaderSnippet.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/attHeader_new.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/att_abs_tpls.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/checklist-model.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/checklist-model.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/d7100892.modernizr.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/ebiz_keepalive.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/footer.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/footer_new.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/gestures.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/ng_base.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/ui-charts-tpls.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/fusion/images/login_button.gif create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/js/jquery-1.10.2.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/js/jquery-ui.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/js/jquery.mask.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/WEB-INF/web.xml create mode 100644 ecomp-portal-BE-common/src/main/webapp/images/cache/README.txt create mode 100644 ecomp-portal-BE-common/src/main/webapp/index.jsp create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-animate.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-animate.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-aria.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-aria.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-cookies.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-cookies.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-csp.css create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-loader.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-loader.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-message-format.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-message-format.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-messages.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-messages.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-mocks.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-resource.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-resource.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-route.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-route.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-sanitize.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-sanitize.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-scenario.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-touch.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular-touch.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/angular.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/errors.json create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/version.json create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-1.5/version.txt create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-ui/ui-bootstrap-tpls-1.1.2.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-ui/ui-bootstrap-tpls-1.2.4.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/angular-ui/ui-sortable/v0.13.4/sortable.min.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/app/fusion/external/utils/js/browserCheck.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/ebz/angular_js/angular-sanitize.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/ebz/angular_js/angular.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/ebz/angular_js/app.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/ebz/angular_js/ebiz_keepalive.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/ebz/angular_js/gestures.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/fusion/images/ecomp-login.jpg create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/fusion/images/ecomp.png create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/fusion/images/ecomp_trans.png create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/js/jquery-1.10.2.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/js/jquery-ui.js create mode 100644 ecomp-portal-BE-common/src/main/webapp/static/js/jquery.mask.min.js create mode 100644 ecomp-portal-BE-common/src/test/java/org/openecomp/portalapp/portal/controller/SharedContextRestClient.java create mode 100644 ecomp-portal-BE-common/src/test/java/org/openecomp/portalapp/portal/controller/SharedContextRestControllerTest.java create mode 100644 ecomp-portal-BE-common/src/test/java/org/openecomp/portalapp/portal/controller/SharedContextTestProperties.java create mode 100644 ecomp-portal-BE-common/src/test/java/org/openecomp/portalapp/portal/controller/shared-context-test.properties create mode 100644 ecomp-portal-BE-common/src/test/java/org/openecomp/portalapp/portal/listener/HealthMonitorTest.java create mode 100644 ecomp-portal-BE-common/src/test/java/org/openecomp/portalapp/portal/utils/EcompPortalUtilsTest.java (limited to 'ecomp-portal-BE-common') diff --git a/ecomp-portal-BE-common/.gitignore b/ecomp-portal-BE-common/.gitignore new file mode 100644 index 00000000..0580e087 --- /dev/null +++ b/ecomp-portal-BE-common/.gitignore @@ -0,0 +1,5 @@ +/catalina.base_IS_UNDEFINED/ +/target/ +.classpath +.project +.settings/ diff --git a/ecomp-portal-BE-common/license/licenses.properties b/ecomp-portal-BE-common/license/licenses.properties new file mode 100644 index 00000000..4b0e28e7 --- /dev/null +++ b/ecomp-portal-BE-common/license/licenses.properties @@ -0,0 +1 @@ +my_license=My license diff --git a/ecomp-portal-BE-common/license/my_license/header.txt b/ecomp-portal-BE-common/license/my_license/header.txt new file mode 100644 index 00000000..c026b6b7 --- /dev/null +++ b/ecomp-portal-BE-common/license/my_license/header.txt @@ -0,0 +1,11 @@ +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/ecomp-portal-BE-common/license/my_license/license.txt b/ecomp-portal-BE-common/license/my_license/license.txt new file mode 100644 index 00000000..bf326153 --- /dev/null +++ b/ecomp-portal-BE-common/license/my_license/license.txt @@ -0,0 +1,4 @@ +name=my_license +description= +licenseURL= +headerURL= \ No newline at end of file diff --git a/ecomp-portal-BE-common/pom.xml b/ecomp-portal-BE-common/pom.xml new file mode 100644 index 00000000..9fb41d31 --- /dev/null +++ b/ecomp-portal-BE-common/pom.xml @@ -0,0 +1,666 @@ + + 4.0.0 + org.openecomp.portal + ecompportal-be-common + war + + 1.1.0-SNAPSHOT + + + 4.2.0.RELEASE + 4.3.11.Final + 1.0.0 + 2.7.4 + 1.1.0-SNAPSHOT + UTF-8 + + true + https://nexus.onap.org + /content/repositories/snapshots/ + /content/repositories/releases/ + /content/repositories/staging/ + + + + + + ecomp-releases + OpenECOMP - Release Repository + ${nexusproxy}/${releaseNexusPath} + + + + ecomp-snapshots + OpenECOMP - Snapshot Repository + ${nexusproxy}/${snapshotNexusPath} + + + + ecomp-staging + OpenECOMP - Staging Repository + ${nexusproxy}/${stagingNexusPath} + + + + + + + doclint-java8-disable + + [1.8,) + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + -Xdoclint:none + + + + + + + + + + + ${project.artifactId} + + + + + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + + attach-javadocs + + jar + + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.0 + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.0.0 + + + true + + WEB-INF/lib/*.jar + + + + + + + + + + src/test/java + + **/*Test*.* + + + + + + + src/main/resources + + **/* + + + + + src/main/java + + **/*.xml + **/*.properties + + + + + + + + + com.att.eelf + eelf-core + ${eelf.version} + + + com.google.code.gson + gson + 2.5 + + + + + org.springframework + spring-core + ${springframework.version} + + + org.springframework + spring-web + ${springframework.version} + + + org.springframework + spring-webmvc + ${springframework.version} + + + org.springframework + spring-tx + ${springframework.version} + + + org.springframework + spring-context-support + ${springframework.version} + + + org.springframework + spring-orm + ${springframework.version} + + + org.springframework + spring-test + ${springframework.version} + + + org.springframework.boot + spring-boot-starter + 1.3.0.RELEASE + + + org.slf4j + log4j-over-slf4j + + + ch.qos.logback + logback-classic + + + + + + + org.hibernate + hibernate-core + ${hibernate.version} + + + org.hibernate + hibernate-validator + 5.1.3.Final + + + + + dom4j + dom4j + 1.6.1 + + + jaxme + jaxme-api + + + jaxen + jaxen + + + msv + xsdlib + + + msv + relaxngDatatype + + + pull-parser + pull-parser + + + xpp3 + xpp3 + + + stax + stax-api + + + + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + javax.servlet.jsp + javax.servlet.jsp-api + 2.3.1 + + + javax.servlet + jstl + 1.2 + + + + + + + org.apache.tiles + tiles-core + 3.0.5 + + + org.apache.tiles + tiles-jsp + 3.0.5 + + + org.apache.cxf + cxf-rt-rs-client + 3.0.0-milestone1 + + + + + com.fasterxml.jackson.core + jackson-annotations + ${fasterxml.version} + + + com.fasterxml.jackson.core + jackson-core + ${fasterxml.version} + + + com.fasterxml.jackson.core + jackson-databind + ${fasterxml.version} + + + + postgresql + postgresql + 9.1-901-1.jdbc4 + + + + + org.mariadb.jdbc + mariadb-java-client + 1.5.8 + + + + + org.elasticsearch + elasticsearch + 2.2.0 + + + io.searchbox + jest + 2.0.0 + + + + org.apache.jcs + jcs + 1.3 + + + * + * + + + + + + + org.apache.tomcat + tomcat-websocket + 8.0.28 + provided + + + + concurrent + concurrent + 1.3.2 + + + * + * + + + + + + + commons-lang + commons-lang + 2.6 + + + jfree + jfreechart + 1.0.13 + + + + org.apache.poi + poi + 3.5-FINAL + + + commons-logging + commons-logging + + + log4j + log4j + + + + + org.apache.poi + poi-ooxml + 3.5-FINAL + + + commons-logging + commons-logging + + + log4j + log4j + + + + + org.apache.poi + poi-scratchpad + 3.5-FINAL + + + commons-logging + commons-logging + + + log4j + log4j + + + + + org.apache.poi + poi-contrib + 3.5-FINAL + + + commons-logging + commons-logging + + + log4j + log4j + + + + + + + com.lowagie + itext + 2.0.8 + + + + org.quartz-scheduler + quartz + 2.2.1 + + + + c3p0 + c3p0 + + + + + + org.bouncycastle + bcprov-jdk16 + 1.45 + + + + commons-codec + commons-codec + 1.9 + + + + com.att.nsa + cambriaClient + 0.0.1 + + + commons-logging + commons-logging + + + log4j + log4j + + + log4j + apache-log4j-extras + + + org.slf4j + slf4j-log4j12 + + + + + + org.openecomp.ecompsdkos + epsdk-fw + ${epsdk.version} + + + commons-logging + commons-logging + + + log4j + log4j + + + log4j + apache-log4j-extras + + + org.slf4j + slf4j-log4j12 + + + + + org.openecomp.ecompsdkos + epsdk-core + ${epsdk.version} + + + mysql + mysql-connector-java + + + + + org.openecomp.ecompsdkos + epsdk-app-common + ${epsdk.version} + + + org.openecomp.ecompsdkos + epsdk-workflow + ${epsdk.version} + + + + + + org.openecomp.ecompsdkos + epsdk-analytics + ${epsdk.version} + + + + + org.apache.httpcomponents + httpclient + 4.5.2 + + + + + junit + junit + 4.11 + test + + + + commons-beanutils + commons-beanutils + 1.9.2 + + + + com.ecwid.consul + consul-api + 1.2.1 + + + + com.orbitz.consul + consul-client + 0.13.8 + + + + commons-fileupload + commons-fileupload + 1.3 + + + + io.springfox + springfox-swagger2 + 2.5.0 + + + + io.springfox + springfox-swagger-ui + 2.5.0 + + + + + + + diff --git a/ecomp-portal-BE-common/src/main/java/jarutil/ExtractJar.java b/ecomp-portal-BE-common/src/main/java/jarutil/ExtractJar.java new file mode 100644 index 00000000..76d4b181 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/jarutil/ExtractJar.java @@ -0,0 +1,204 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package jarutil; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.util.ArrayList; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +public class ExtractJar { + + /** + * @param args + */ + + public static int bufferSize = 8192; + public static String JARFILE = "raptor_upgrade.jar"; + + public static void main(String[] args) throws Exception { + if(args.length > 0 && args[0]!=null && args[0].length()>0) + extractFilesFromJar(args[0]); + else { + System.out.println("Current Directory is taken as webapp path"); + String currentDir = new File(".").getAbsolutePath(); + extractFilesFromJar(currentDir); + } + } + + public static void readJar(File jarFile) throws Exception{ + JarInputStream in = new JarInputStream(new FileInputStream(jarFile)); + ArrayList entries = new ArrayList( ); + JarEntry je; + while((je=in.getNextJarEntry( ))!=null){ + if( je.isDirectory() == false ) { + if(je.getName().startsWith("att/raptor/config/")) { + //while((je=in.getNextJarEntry( ))!=null) { + System.out.println(je.getName() + " " + je.getTime()); + //} + + } + } + } + in.close( ); + } + + public static void extractFilesFromJar(String directory) throws IOException{ + //JarFile jar = new JarFile(jarFile); + Class clazz = ExtractJar.class; + String classContainer = clazz.getProtectionDomain().getCodeSource().getLocation().toString(); + //System.out.println("classContainer ---------> " + classContainer); + URL jarUrl = clazz.getProtectionDomain().getCodeSource().getLocation(); + + JarInputStream entryStream = new JarInputStream(jarUrl.openStream()); + JarEntry entry; + while(true) { + entry = entryStream.getNextJarEntry(); + if(entry == null) + break; + if(entry.getName().indexOf("jarutil") < 0) { + System.out.println(entry.getName()); + File file=new File(directory,entry.getName()); + if(entry.isDirectory()) { + if (!file.exists()) + file.mkdirs(); + } else { + // make directory (some jars don't list dirs) + File dir = new File(file.getParent()); + if (!dir.exists()) + dir.mkdirs(); + if(file.exists()) file.delete(); + // Make file + FileOutputStream fout = new FileOutputStream(file); + copy(entryStream,fout); + fout.close(); + + // touch the file. + if (entry.getTime()>=0) + file.setLastModified(entry.getTime()); + } + + } + entryStream.closeEntry(); + } + entryStream.close(); + System.out.println("************************************************"); + System.out.println("* *"); + System.out.println("* *"); + System.out.println("* RAPTOR SETUP COMPLETE. *"); + System.out.println("* *"); + System.out.println("* Thank you for upgrading. *"); + System.out.println("* *"); + System.out.println("************************************************"); + } + + + public static void copy(InputStream in, OutputStream out, long byteCount) + throws IOException { + byte buffer[] = new byte[bufferSize]; + int len=bufferSize; + if (byteCount>=0) { + while (byteCount>0) { + if (byteCount=0) { + while (byteCount>0) { + if (byteCount menu; + private Set businessDirectMenu; + + /** + * getLoginId + * + * @return String + */ + public String getLoginId() { + return loginId; + } + + /** + * getLoginPwd + * + * @return String + */ + public String getLoginPwd() { + return loginPwd; + } + + /** + * getMenu + * + * @return Set + */ + public Set getMenu() { + return menu; + } + + /** + * getUser + * + * @return User + */ + public EPUser getUser() { + return user; + } + + /** + * getHrid + * + * @return String + */ + public String getHrid() { + return hrid; + } + + /** + * getSiteAccess + * + * @return String + */ + public String getSiteAccess() { + return siteAccess; + } + + /** + * getBusinessDirectMenu + * + * @return Set + */ + public Set getBusinessDirectMenu() { + return businessDirectMenu; + } + + /** + * getLoginErrorMessage + * + * @return String + */ + public String getLoginErrorMessage() { + return loginErrorMessage; + } + + public String getOrgUserId() { + return orgUserId; + } + + /** + * setLoginId + * + * @param loginId String + */ + public void setLoginId(String loginId) { + this.loginId = loginId; + } + + /** + * setLoginPwd + * + * @param loginPwd String + */ + public void setLoginPwd(String loginPwd) { + this.loginPwd = loginPwd; + } + + public void setMenu(Set menu) { + this.menu = menu; + } + + /** + * setUser + * + * @param user User + */ + public void setUser(EPUser user) { + this.user = user; + } + + /** + * setHrid + * + * @param hrid String + */ + public void setHrid(String hrid) { + this.hrid = hrid; + } + + /** + * setSiteAccess + * + * @param siteAccess String + */ + public void setSiteAccess(String siteAccess) { + this.siteAccess = siteAccess; + } + + /** + * setBusinessDirectMenu + * + * @param businessDirectMenu Set + */ + public void setBusinessDirectMenu(Set businessDirectMenu) { + this.businessDirectMenu = businessDirectMenu; + } + + /** + * setLoginErrorMessage + * + * @param loginErrorMessage String + */ + public void setLoginErrorMessage(String loginErrorMessage) { + this.loginErrorMessage = loginErrorMessage; + } + + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } +} + diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/command/PostSearchBean.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/command/PostSearchBean.java new file mode 100644 index 00000000..9861554a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/command/PostSearchBean.java @@ -0,0 +1,373 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.command; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalsdk.core.command.support.SearchBase; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +@JsonSerialize +public class PostSearchBean extends SearchBase { + + private EPUser user = null; + private EPUser userOrig = null; + private String[] selected; + private String[] postHrid; + private String[] postOrgUserId; + private String[] postFirstName; + private String[] postLastName; + private String[] postOrgCode; + private String[] postPhone; + private String[] postEmail; + private String[] postAddress1; + private String[] postAddress2; + private String[] postCity; + private String[] postState; + private String[] postZipCode; + private String[] postLocationClli; + private String[] postBusinessCountryCode; + private String[] postBusinessCountryName; + private String[] postDepartment; + private String[] postDepartmentName; + private String[] postBusinessUnit; + private String[] postBusinessUnitName; + private String[] postJobTitle; + private String[] postOrgManagerUserId; + private String[] postCommandChain; + private String[] postCompanyCode; + private String[] postCompany; + private String[] postCostCenter; + private String[] postSiloStatus; + private String[] postFinancialLocCode; + + + public PostSearchBean() { + this(null); + } // PostSearchBean + + public PostSearchBean(List items) { + super(items); + + user = new EPUser(); + userOrig = new EPUser(); + + setSortBy1(""); + setSortBy1Orig(""); + + //setSortByList(...); + } // PostSearchBean + + + public String getFirstName() { return user.getFirstName(); } + public String getLastName() { return user.getLastName(); } + public String getHrid() { return user.getHrid(); } + public String getOrgUserId() { return user.getOrgUserId(); } + public String getOrgCode() { return user.getOrgCode(); } + public String getEmail() { return user.getEmail(); } + public String getOrgManagerUserId() { return user.getOrgManagerUserId(); } + + public String getFirstNameOrig() { return user.getFirstName(); } + public String getLastNameOrig() { return user.getLastName(); } + public String getHridOrig() { return user.getHrid(); } + public String getOrgUserIdOrig() { return user.getOrgUserId(); } + public String getOrgCodeOrig() { return user.getOrgCode(); } + public String getEmailOrig() { return user.getEmail(); } + public String getOrgManagerUserIdOrig() { return user.getOrgManagerUserId(); } + + + public EPUser getUser() { return user; } + + public String[] getPostEmail() { + return postEmail; + } + + public String[] getPostFirstName() { + return postFirstName; + } + + public String[] getPostHrid() { + return postHrid; + } + + public String[] getPostLastName() { + return postLastName; + } + + public String[] getPostOrgCode() { + return postOrgCode; + } + + public String[] getPostPhone() { + return postPhone; + } + + public String[] getPostOrgUserId() { + return postOrgUserId; + } + + public String[] getSelected() { + return selected; + } + + public String[] getPostAddress1() { + return postAddress1; + } + + public String[] getPostBusinessCountryCode() { + return postBusinessCountryCode; + } + + public String[] getPostCity() { + return postCity; + } + + public String[] getPostCommandChain() { + return postCommandChain; + } + + public String[] getPostCompany() { + return postCompany; + } + + public String[] getPostCompanyCode() { + return postCompanyCode; + } + + public String[] getPostDepartment() { + return postDepartment; + } + + public String[] getPostDepartmentName() { + return postDepartmentName; + } + + public String[] getPostBusinessCountryName() { + return postBusinessCountryName; + } + + public String[] getPostJobTitle() { + return postJobTitle; + } + + public String[] getPostLocationClli() { + return postLocationClli; + } + + public String[] getPostManagerUserId() { + return postOrgManagerUserId; + } + + public String[] getPostState() { + return postState; + } + + public String[] getPostZipCode() { + return postZipCode; + } + + public void setFirstName(String value) { user.setFirstName(value); } + public void setLastName(String value) { user.setLastName(value); } + public void setHrid(String value) { user.setHrid(value); } + public void setOrgUserId(String value) { user.setOrgUserId(value); } + public void setOrgCode(String value) { user.setOrgCode(value); } + public void setEmail(String value) { user.setEmail(value); } + public void setOrgManagerUserId(String value) { user.setOrgManagerUserId(value); } + + public void setFirstNameOrig(String value) { userOrig.setFirstName(value); } + public void setLastNameOrig(String value) { userOrig.setLastName(value); } + public void setHridOrig(String value) { userOrig.setHrid(value); } + public void setOrgUserIdOrig(String value) { userOrig.setOrgUserId(value); } + public void setOrgCodeOrig(String value) { userOrig.setOrgCode(value); } + public void setEmailOrig(String value) { userOrig.setEmail(value); } + public void setOrgManagerUserIdOrig(String value) { userOrig.setOrgManagerUserId(value); } + + public void setUser(EPUser value) { this.user = value; } + + public void setPostEmail(String[] postEmail) { + this.postEmail = postEmail; + } + + public void setPostFirstName(String[] postFirstName) { + this.postFirstName = postFirstName; + } + + public void setPostHrid(String[] postHrid) { + this.postHrid = postHrid; + } + + public void setPostLastName(String[] postLastName) { + this.postLastName = postLastName; + } + + public void setPostOrgCode(String[] postOrgCode) { + this.postOrgCode = postOrgCode; + } + + public void setPostPhone(String[] postPhone) { + this.postPhone = postPhone; + } + + public void setPostUserId(String[] postOrgUserId) { + this.postOrgUserId = postOrgUserId; + } + + public void setSelected(String[] selected) { + this.selected = selected; + } + + public void setPostAddress1(String[] postAddress1) { + this.postAddress1 = postAddress1; + } + + public void setPostBusinessCountryCode(String[] postBusinessCountryCode) { + this.postBusinessCountryCode = postBusinessCountryCode; + } + + public void setPostCity(String[] postCity) { + this.postCity = postCity; + } + + public void setPostCommandChain(String[] postCommandChain) { + this.postCommandChain = postCommandChain; + } + + public void setPostCompany(String[] postCompany) { + this.postCompany = postCompany; + } + + public void setPostCompanyCode(String[] postCompanyCode) { + this.postCompanyCode = postCompanyCode; + } + + public void setPostDepartment(String[] postDepartment) { + this.postDepartment = postDepartment; + } + + public void setPostDepartmentName(String[] postDepartmentName) { + this.postDepartmentName = postDepartmentName; + } + + public void setPostBusinessCountryName(String[] postBusinessCountryName) { + this.postBusinessCountryName = postBusinessCountryName; + } + + public void setPostJobTitle(String[] postJobTitle) { + this.postJobTitle = postJobTitle; + } + + public void setPostLocationClli(String[] postLocationClli) { + this.postLocationClli = postLocationClli; + } + + public void setPostManagerUserId(String[] postOrgManagerUserId) { + this.postOrgManagerUserId = postOrgManagerUserId; + } + + public void setPostState(String[] postState) { + this.postState = postState; + } + + public void setPostZipCode(String[] postZipCode) { + this.postZipCode = postZipCode; + } + + public String[] getPostAddress2() { + return postAddress2; + } + + public void setPostAddress2(String[] postAddress2) { + this.postAddress2 = postAddress2; + } + + public EPUser getUserOrig() { + return userOrig; + } + + public void setUserOrig(EPUser userOrig) { + this.userOrig = userOrig; + } + + public String[] getPostBusinessUnit() { + return postBusinessUnit; + } + + public void setPostBusinessUnit(String[] postBusinessUnit) { + this.postBusinessUnit = postBusinessUnit; + } + + public String[] getPostBusinessUnitName() { + return postBusinessUnitName; + } + + public void setPostBusinessUnitName(String[] postBusinessUnitName) { + this.postBusinessUnitName = postBusinessUnitName; + } + + public String[] getPostCostCenter() { + return postCostCenter; + } + + public void setPostCostCenter(String[] postCostCenter) { + this.postCostCenter = postCostCenter; + } + + public String[] getPostSiloStatus() { + return postSiloStatus; + } + + public void setPostSiloStatus(String[] postSiloStatus) { + this.postSiloStatus = postSiloStatus; + } + + public String[] getPostFinancialLocCode() { + return postFinancialLocCode; + } + + public void setPostFinancialLocCode(String[] postFinancialLocCode) { + this.postFinancialLocCode = postFinancialLocCode; + } + + public void resetSearch() { + super.resetSearch(); + setUser(new EPUser()); + } // resetSearch + + + public boolean isCriteriaUpdated() { + if(user==null&&userOrig==null) + return false; + else if(user==null||userOrig==null) + return true; + else + return (! ( + Utilities.nvl(user.getFirstName()).equals(Utilities.nvl(userOrig.getFirstName()))&& + Utilities.nvl(user.getLastName()).equals(Utilities.nvl(userOrig.getLastName()))&& + //Utilities.nvl(user.getHrid()).equals(Utilities.nvl(userOrig.getHrid()))&& + Utilities.nvl(user.getOrgUserId()).equals(Utilities.nvl(userOrig.getOrgUserId()))&& + Utilities.nvl(user.getOrgCode()).equals(Utilities.nvl(userOrig.getOrgCode()))&& + Utilities.nvl(user.getEmail()).equals(Utilities.nvl(userOrig.getEmail()))&& + Utilities.nvl(user.getOrgManagerUserId()).equals(Utilities.nvl(userOrig.getOrgManagerUserId()))&& + true)); + } // isCriteriaUpdated + +} // PostSearchBean diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/config/SwaggerConfig.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/config/SwaggerConfig.java new file mode 100644 index 00000000..7082fe78 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/config/SwaggerConfig.java @@ -0,0 +1,58 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + @Bean + public Docket api(){ + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.regex("/auxapi/.*")) + .build() + .apiInfo(apiInfo()); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("ECOMP Portal API Documentation") + .description("ECOMP Portal API Documentation") + .version("1.2.6") + .termsOfServiceUrl("http://terms-of-services.url") + .license("LICENSE") + .licenseUrl("http://url-to-license.com") + .build(); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/config/SwaggerMVCConfig.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/config/SwaggerMVCConfig.java new file mode 100644 index 00000000..7e2d4540 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/config/SwaggerMVCConfig.java @@ -0,0 +1,37 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@Configuration +@EnableWebMvc +public class SwaggerMVCConfig extends WebMvcConfigurerAdapter { + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPFusionBaseController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPFusionBaseController.java new file mode 100644 index 00000000..986f6387 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPFusionBaseController.java @@ -0,0 +1,106 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.controller; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.util.EPUserUtils; +import org.openecomp.portalsdk.core.controller.FusionBaseController; +import org.openecomp.portalsdk.core.domain.MenuData; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ModelAttribute; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@Controller +public abstract class EPFusionBaseController extends FusionBaseController { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPFusionBaseController.class); + + + + @Override + public boolean isAccessible() { + return true; + } + + public boolean isRESTfulCall() { + return true; + } + + @ModelAttribute("menu") + public Map messages(HttpServletRequest request) { + HttpSession session = null; + Map model = new HashMap(); + session = request.getSession(); + EPUser user = EPUserUtils.getUserSession(request); + if (session != null && user != null) { + @SuppressWarnings("unchecked") + Set menuResult = (Set) session + .getAttribute(SystemProperties.getProperty(SystemProperties.APPLICATION_MENU_ATTRIBUTE_NAME)); + try { + model = setMenu(menuResult); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } + } + + return model; + } + + public Map setMenu(Set menuResult) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + List> childItemList = new ArrayList>(); + ; + List parentList = new ArrayList(); + ; + Map model = new HashMap(); + for (MenuData menu : menuResult) { + MenuData parentData = new MenuData(); + parentData.setLabel(menu.getLabel()); + parentData.setAction(menu.getAction()); + parentData.setImageSrc(menu.getImageSrc()); + parentList.add(parentData); + List tempList = new ArrayList(); + for (Object o : menu.getChildMenus()) { + MenuData m = (MenuData) o; + MenuData data = new MenuData(); + data.setLabel(m.getLabel()); + data.setAction(m.getAction()); + data.setImageSrc(m.getImageSrc()); + tempList.add(data); + } + childItemList.add(tempList); + } + model.put("childItemList", childItemList != null ? mapper.writeValueAsString(childItemList) : ""); + model.put("parentList", parentList != null ? mapper.writeValueAsString(parentList) : ""); + return model; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPRestrictedBaseController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPRestrictedBaseController.java new file mode 100644 index 00000000..b3bef781 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPRestrictedBaseController.java @@ -0,0 +1,52 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.controller; + +public class EPRestrictedBaseController extends EPFusionBaseController{ + + protected String viewName; + private String exceptionView; + + @Override + public boolean isAccessible() { + return false; + } + + @Override + public boolean isRESTfulCall(){ + return false; + } + + protected String getViewName() { + return viewName; + } + + protected void setViewName(String viewName) { + this.viewName = viewName; + } + + public String getExceptionView() { + return (exceptionView == null) ? "runtime_error_handler" : exceptionView; + } + + public void setExceptionView(String exceptionView) { + this.exceptionView = exceptionView; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPRestrictedRESTfulBaseController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPRestrictedRESTfulBaseController.java new file mode 100644 index 00000000..7b1be46c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPRestrictedRESTfulBaseController.java @@ -0,0 +1,54 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.controller; + +public class EPRestrictedRESTfulBaseController extends EPFusionBaseController{ + + protected String viewName; + private String exceptionView; + + @Override + public boolean isAccessible() { + return false; + } + + @Override + public boolean isRESTfulCall(){ + return true; + } + + protected String getViewName() { + return viewName; + } + + protected void setViewName(String viewName) { + this.viewName = viewName; + } + + public String getExceptionView() { + return (exceptionView == null) ? "runtime_error_handler" : exceptionView; + } + + public void setExceptionView(String exceptionView) { + this.exceptionView = exceptionView; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPUnRestrictedBaseController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPUnRestrictedBaseController.java new file mode 100644 index 00000000..f16c1b94 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/EPUnRestrictedBaseController.java @@ -0,0 +1,42 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.controller; + +public class EPUnRestrictedBaseController extends EPFusionBaseController{ + protected String viewName; + + @Override + public boolean isAccessible() { + return true; + } + + @Override + public boolean isRESTfulCall(){ + return false; + } + + protected String getViewName() { + return viewName; + } + + protected void setViewName(String viewName) { + this.viewName = viewName; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/sessionmgt/SessionCommunicationController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/sessionmgt/SessionCommunicationController.java new file mode 100644 index 00000000..eda094b5 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/controller/sessionmgt/SessionCommunicationController.java @@ -0,0 +1,72 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.controller.sessionmgt; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.EPRestrictedRESTfulBaseController; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.utils.PortalConstants; +import org.openecomp.portalapp.service.sessionmgt.ManageService; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.annotations.ApiOperation; + + +@RestController +@RequestMapping(PortalConstants.REST_AUX_API) +@Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class SessionCommunicationController extends EPRestrictedRESTfulBaseController { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SessionCommunicationController.class); + + @Autowired + private ManageService manageService; + + protected boolean isAuxRESTfulCall(){ + return true; + } + + @ApiOperation(value = "Gets session slot-check interval, a duration in milliseconds.", + response = Integer.class) + @RequestMapping(value={"/getSessionSlotCheckInterval"}, method = RequestMethod.GET, produces = "application/json") + public Integer getSessionSlotCheckInterval(HttpServletRequest request, HttpServletResponse response) throws Exception { + return manageService.fetchSessionSlotCheckInterval(); + } + + @ApiOperation(value = "Extends session timeout values for all on-boarded applications.", + response = Boolean.class) + @RequestMapping(value={"/extendSessionTimeOuts"}, method = RequestMethod.POST) + public Boolean extendSessionTimeOuts(HttpServletRequest request, HttpServletResponse response, @RequestParam String sessionMap) throws Exception { + manageService.extendSessionTimeOuts(sessionMap); + + return true; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/controller/RolesApprovalSystemController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/controller/RolesApprovalSystemController.java new file mode 100644 index 00000000..e0fa523a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/controller/RolesApprovalSystemController.java @@ -0,0 +1,143 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.externalsystemapproval.controller; + +import java.util.ArrayList; + +import javax.servlet.http.HttpServletRequest; + +import org.openecomp.portalapp.externalsystemapproval.model.ExternalSystemRoleApproval; +import org.openecomp.portalapp.externalsystemapproval.model.ExternalSystemUser; +import org.openecomp.portalapp.portal.controller.BasicAuthenticationController; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.UserRolesService; +import org.openecomp.portalapp.portal.utils.PortalConstants; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.annotations.ApiOperation; + +@RestController +@RequestMapping(PortalConstants.REST_AUX_API) +@Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class RolesApprovalSystemController implements BasicAuthenticationController { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RolesApprovalSystemController.class); + + @Autowired + private UserRolesService userRolesService; + + /** + * Creates an application user with the specified roles. + * + * @param request + * @param extSysUser + * @return PortalRestResponse with appropriate status value and message + */ + @ApiOperation(value = "Creates an application user with the specified roles.", response = PortalRestResponse.class) + @RequestMapping(value = { "/userProfile" }, method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse postUserProfile(HttpServletRequest request, + @RequestBody ExternalSystemUser extSysUser) { + try { + validateExtSystemUser(extSysUser, true); + String response = userRolesService.setAppWithUserRoleStateForUser(extSysUser); + return new PortalRestResponse(PortalRestStatusEnum.OK, "Saved Successfully", response); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "postUserProfile failed", e); + return new PortalRestResponse(PortalRestStatusEnum.ERROR, e.toString(), null); + } + } + + /** + * Updates an application user to have only the specified roles. + * + * @param request + * @param extSysUser + * @return PortalRestResponse with appropriate status value and message + */ + @ApiOperation(value = "Updates an application user to have only the specified roles.", response = PortalRestResponse.class) + @RequestMapping(value = { "/userProfile" }, method = RequestMethod.PUT, produces = "application/json") + public PortalRestResponse putUserProfile(HttpServletRequest request, + @RequestBody ExternalSystemUser extSysUser) { + try { + validateExtSystemUser(extSysUser, true); + String response = userRolesService.setAppWithUserRoleStateForUser(extSysUser); + return new PortalRestResponse(PortalRestStatusEnum.OK, "Updated Successfully", response); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "putUserProfile failed", e); + return new PortalRestResponse(PortalRestStatusEnum.ERROR, e.toString(), null); + } + } + + /** + * Deletes an application user by removing all assigned roles. + * + * @param request + * @param extSysUser + * This object must have zero roles. + * @return PortalRestResponse with appropriate status value and message + */ + @ApiOperation(value = "Processes a request to delete one or more application roles for one specified user who has roles.", response = PortalRestResponse.class) + @RequestMapping(value = { "/userProfile" }, method = RequestMethod.DELETE, produces = "application/json") + public PortalRestResponse deleteUserProfile(HttpServletRequest request, + @RequestBody ExternalSystemUser extSysUser) { + try { + validateExtSystemUser(extSysUser, false); + // Ignore any roles that might be mistakenly present in the request + extSysUser.setRoles(new ArrayList()); + String result = userRolesService.setAppWithUserRoleStateForUser(extSysUser); + return new PortalRestResponse(PortalRestStatusEnum.OK, "Deleted Successfully", result); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "deleteUserProfile failed", e); + return new PortalRestResponse(PortalRestStatusEnum.ERROR, e.toString(), null); + } + } + + /** + * Checks for presence of required fields. + * + * @param extSysUser + * @param rolesRequired + * If true, checks whether roles are present + * @throws Exception + * If any field is missing. + */ + private void validateExtSystemUser(ExternalSystemUser extSysUser, boolean rolesRequired) throws Exception { + if (extSysUser.getLoginId() == null) + throw new Exception("Request has no login ID"); + if (extSysUser.getApplicationName() == null) + throw new Exception("Request has no application name"); + if (extSysUser.getMyloginrequestId() == null) + throw new Exception("Request has no request ID"); + if (rolesRequired && (extSysUser.getRoles() == null || extSysUser.getRoles().size() == 0)) + throw new Exception("Request has no roles"); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/model/ExternalSystemRoleApproval.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/model/ExternalSystemRoleApproval.java new file mode 100644 index 00000000..f958d2b5 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/model/ExternalSystemRoleApproval.java @@ -0,0 +1,41 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.externalsystemapproval.model; + +import java.io.Serializable; + + +public class ExternalSystemRoleApproval implements Serializable{ + + + private static final long serialVersionUID = 6048830318039958615L; + + private String roleName ; + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/model/ExternalSystemUser.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/model/ExternalSystemUser.java new file mode 100644 index 00000000..f3ccd14a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/externalsystemapproval/model/ExternalSystemUser.java @@ -0,0 +1,74 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.externalsystemapproval.model; + + +import java.util.ArrayList; +import java.util.List; + +public class ExternalSystemUser { + + private String loginId; + + private String applicationName; + + private String myloginrequestId; + + private List roles; + + public ExternalSystemUser() { + roles = new ArrayList(); + } + + public String getApplicationName() { + return applicationName; + } + + public void setApplicationName(String applicationName) { + this.applicationName = applicationName; + } + + public String getLoginId() { + return loginId; + } + + public void setLoginId(String loginId) { + this.loginId = loginId; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + public String getMyloginrequestId() { + return myloginrequestId; + } + + public void setMyloginrequestId(String myloginrequestId) { + this.myloginrequestId = myloginrequestId; + } + + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/lm/FusionLicenseManagerImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/lm/FusionLicenseManagerImpl.java new file mode 100644 index 00000000..e932995f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/lm/FusionLicenseManagerImpl.java @@ -0,0 +1,88 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.lm; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletContext; + +import org.openecomp.portalsdk.core.lm.FusionLicenseManager; +import org.springframework.stereotype.Component; + +/* + * Please note that this class is not being used; its a dummy stub to have a qualifying bean for the interface. + */ + +@Component +public class FusionLicenseManagerImpl implements FusionLicenseManager { + + @Override + public void initKeyStoreParam() { + + } + + @Override + public void initCipherParam() { + + } + + @Override + public void initLicenseParam() { + + } + + @Override + public void doInitWork() { + + } + + @Override + public int installLicense() { + return 0; + } + + @Override + public synchronized int verifyLicense(ServletContext context) { + return 0; + } + + @Override + public void generateLicense(Map clientInfoMap, List ipAddressList) throws Exception { + + } + + @Override + public String nvl(String s) { + return null; + } + + @Override + public Date getExpiredDate() { + return null; + } + + @Override + public void setExpiredDate(Date expiredDate) { + + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/lm/LicenseableClassImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/lm/LicenseableClassImpl.java new file mode 100644 index 00000000..91fe7870 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/lm/LicenseableClassImpl.java @@ -0,0 +1,62 @@ +/*- + * ================================================================================ + * eCOMP Portal SDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.lm; + +import java.io.FileNotFoundException; +import java.io.InputStream; + +import org.openecomp.portalsdk.core.lm.LicenseableClass; + +/* + * Please note that this class is not being used; its a dummy stub to have a qualifying bean for the interface. + */ + +public class LicenseableClassImpl implements LicenseableClass { + + public String getApplicationName() { + return ""; + } + + public InputStream getPublicKeystoreAsInputStream() throws FileNotFoundException { + return null; + } + + public String getAlias() { + return ""; + } + + public String getKeyPasswd() { + return ""; + } + + public String getPublicKeystorePassword() { + return ""; + } + + public String getCipherParamPassword() { + return ""; + } + + @SuppressWarnings("rawtypes") + public Class getClassToLicense() { + return this.getClass(); + } +} + diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/model/Result.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/model/Result.java new file mode 100644 index 00000000..21c9c450 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/model/Result.java @@ -0,0 +1,37 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.model; + +public class Result { + private String result; + + public Result(String result) { + this.result = result; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppCatalogController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppCatalogController.java new file mode 100644 index 00000000..a799f496 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppCatalogController.java @@ -0,0 +1,124 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; + +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.AdminRolesService; +import org.openecomp.portalapp.portal.service.EPAppService; +import org.openecomp.portalapp.portal.service.PersUserAppService; +import org.openecomp.portalapp.portal.transport.AppCatalogPersonalization; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.portal.ecomp.model.AppCatalogItem; +import org.openecomp.portalapp.util.EPUserUtils; + +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class AppCatalogController extends EPRestrictedBaseController { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AppCatalogController.class); + + @Autowired + AdminRolesService adminRolesService; + @Autowired + EPAppService appService; + @Autowired + PersUserAppService persUserAppService; + + /** + * RESTful service method to fetch all enabled applications, with details + * about which are accessible to the current user, selected by the current + * user. + * + * @return List of items suitable for display + */ + @RequestMapping(value = { "/portalApi/appCatalog" }, method = RequestMethod.GET, produces = "application/json") + public List getAppCatalog(HttpServletRequest request, HttpServletResponse response) + throws IOException { + EPUser user = EPUserUtils.getUserSession(request); + List appCatalog = null; + try { + if (user == null) { + EcompPortalUtils.setBadPermissions(user, response, "getAppCatalog"); + } else { + if (adminRolesService.isSuperAdmin(user)) + appCatalog = appService.getAdminAppCatalog(user); + else + appCatalog = appService.getUserAppCatalog(user); + EcompPortalUtils.logAndSerializeObject(logger,"/portalApi/getAppCatalog", "GET result =", appCatalog); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Failed in getAppCatalog", e); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString()); + } + return appCatalog; + } + + /** + * RESTful service to accept a user's action made on the application + * catalog. + * + * @param request + * @param selectRequest + * JSON with data including application ID + * @param response + * @return FieldsValidator + * @throws IOException + */ + @RequestMapping(value = { "/portalApi/appCatalog" }, method = RequestMethod.PUT, produces = "application/json") + public FieldsValidator putAppCatalogSelection(HttpServletRequest request, + @RequestBody AppCatalogPersonalization persRequest, HttpServletResponse response) throws IOException { + FieldsValidator result = new FieldsValidator(); + EPApp app = appService.getApp(persRequest.getAppId()); + EPUser user = EPUserUtils.getUserSession(request); + try { + if (app == null || user == null) { + EcompPortalUtils.setBadPermissions(user, response, "putAppCatalogSelection"); + } else { + persUserAppService.setPersUserAppValue(user, app, persRequest.getSelect(), persRequest.getPending()); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Failed in putAppCatalogSelection", e); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString()); + } + result.httpStatusCode = new Long(HttpServletResponse.SC_OK); + return result; + } + +} \ No newline at end of file diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppContactUsController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppContactUsController.java new file mode 100644 index 00000000..9b476e05 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppContactUsController.java @@ -0,0 +1,224 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.json.JSONObject; +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.ecomp.model.AppCategoryFunctionsItem; +import org.openecomp.portalapp.portal.ecomp.model.AppContactUsItem; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.AppContactUsService; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/portalApi/contactus") +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class AppContactUsController extends EPRestrictedBaseController { + + static final String FAILURE = "failure"; + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AppContactUsController.class); + + @Autowired + private AppContactUsService contactUsService; + + /** + * Answers a JSON object with three items from the system.properties file: + * user self-help ticket URL, email for feedback, and Portal info link. + * + * @param request + * @return + */ + @RequestMapping(value = "/feedback", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse getPortalDetails(HttpServletRequest request) { + PortalRestResponse portalRestResponse = null; + try { + final String ticketUrl = SystemProperties.getProperty(EPCommonSystemProperties.USH_TICKET_URL); + final String portalInfoUrl = SystemProperties.getProperty(EPCommonSystemProperties.PORTAL_INFO_URL); + final String feedbackEmail = SystemProperties.getProperty(EPCommonSystemProperties.FEEDBACK_EMAIL_ADDRESS); + HashMap map = new HashMap(); + map.put(EPCommonSystemProperties.USH_TICKET_URL, ticketUrl); + map.put(EPCommonSystemProperties.PORTAL_INFO_URL, portalInfoUrl); + map.put(EPCommonSystemProperties.FEEDBACK_EMAIL_ADDRESS, feedbackEmail); + JSONObject j = new JSONObject(map); + String contactUsPortalResponse = j.toString(); + portalRestResponse = new PortalRestResponse(PortalRestStatusEnum.OK, "success", + contactUsPortalResponse); + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, FAILURE, e.getMessage()); + } + return portalRestResponse; + } + + /** + * Answers the contents of the contact-us table, extended with the + * application name. + * + * @param request + * @return + */ + @RequestMapping(value = "/list", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getAppContactUsList(HttpServletRequest request) { + PortalRestResponse> portalRestResponse = null; + try { + List contents = contactUsService.getAppContactUs(); + portalRestResponse = new PortalRestResponse>(PortalRestStatusEnum.OK, "success", + contents); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAppContactUsList failed", e); + portalRestResponse = new PortalRestResponse>(PortalRestStatusEnum.ERROR, + e.getMessage(), null); + } + return portalRestResponse; + } + + /** + * Answers a list of objects, one per application, extended with available + * data on how to contact that app's organization (possibly none). + * + * @param request + * @return + */ + @RequestMapping(value = "/allapps", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getAppsAndContacts(HttpServletRequest request) { + PortalRestResponse> portalRestResponse = null; + try { + List contents = contactUsService.getAppsAndContacts(); + portalRestResponse = new PortalRestResponse>(PortalRestStatusEnum.OK, "success", + contents); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAllAppsAndContacts failed", e); + portalRestResponse = new PortalRestResponse>(PortalRestStatusEnum.ERROR, + e.getMessage(), null); + } + return portalRestResponse; + } + + /** + * Sorts by category name. + */ + private Comparator appCategoryFunctionsItemComparator = new Comparator() { + @Override + public int compare(AppCategoryFunctionsItem o1, AppCategoryFunctionsItem o2) { + return o1.getCategory().compareTo(o2.getCategory()); + } + }; + + /** + * Answers a list of objects with category-application-function details. Not + * all applications participate in the functional menu. + * + * @param request + * @return + */ + @RequestMapping(value = "/functions", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getAppCategoryFunctions(HttpServletRequest request) { + PortalRestResponse> portalRestResponse = null; + try { + List contents = contactUsService.getAppCategoryFunctions(); + // logger.debug(EELFLoggerDelegate.debugLogger, + // "getAppCategoryFunctions: result list size is " + + // contents.size()); + Collections.sort(contents, appCategoryFunctionsItemComparator); + portalRestResponse = new PortalRestResponse>(PortalRestStatusEnum.OK, + "success", contents); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAppCategoryFunctions failed", e); + // TODO build JSON error + portalRestResponse = new PortalRestResponse>(PortalRestStatusEnum.ERROR, + e.getMessage(), null); + } + return portalRestResponse; + } + + /** + * Accepts a new application's contact us details. + * + * @param contactUs + * @return + */ + @RequestMapping(value = "/save", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse save(@RequestBody AppContactUsItem contactUs) { + + if (contactUs == null || contactUs.getAppName() == null) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, FAILURE, + "AppName cannot be null or empty"); + + String saveAppContactUs = FAILURE; + try { + saveAppContactUs = contactUsService.saveAppContactUs(contactUs); + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.OK, saveAppContactUs, e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.ERROR, saveAppContactUs, ""); + } + + @RequestMapping(value = "/saveAll", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse save(@RequestBody List contactUsList) { + + String saveAppContactUs = FAILURE; + try { + saveAppContactUs = contactUsService.saveAppContactUs(contactUsList); + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.OK, saveAppContactUs, e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.ERROR, saveAppContactUs, ""); + } + + /** + * Deletes the specified application's contact-us details entry from the + * table. + * + * @param id + * @return + */ + @RequestMapping(value = "/delete/{appid}", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse delete(@PathVariable("appid") Long id) { + + String saveAppContactUs = FAILURE; + try { + saveAppContactUs = contactUsService.deleteContactUs(id); + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.OK, saveAppContactUs, e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.ERROR, saveAppContactUs, ""); + } + +} \ No newline at end of file diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppsController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppsController.java new file mode 100644 index 00000000..7a05d676 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppsController.java @@ -0,0 +1,670 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.AdminUserApplications; +import org.openecomp.portalapp.portal.domain.AppIdAndNameTransportModel; +import org.openecomp.portalapp.portal.domain.AppsResponse; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EcompApp; +import org.openecomp.portalapp.portal.domain.UserRoles; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.AdminRolesService; +import org.openecomp.portalapp.portal.service.EPAppService; +import org.openecomp.portalapp.portal.service.EPLeftMenuService; +import org.openecomp.portalapp.portal.transport.EPAppsManualPreference; +import org.openecomp.portalapp.portal.transport.EPAppsSortPreference; +import org.openecomp.portalapp.portal.transport.EPDeleteAppsManualSortPref; +import org.openecomp.portalapp.portal.transport.EPWidgetsSortPreference; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.LocalRole; +import org.openecomp.portalapp.portal.transport.OnboardingApp; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.util.EPUserUtils; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalsdk.core.web.support.AppUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@EnableAspectJAutoProxy +@EPAuditLog +public class AppsController extends EPRestrictedBaseController { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AppsController.class); + + @Autowired + private AdminRolesService adminRolesService; + + @Autowired + private EPAppService appService; + + @Autowired + private EPLeftMenuService leftMenuService; + + /** + * RESTful service method to fetch all Applications available to current + * user + * + * @return + */ + @RequestMapping(value = { "/portalApi/userApps" }, method = RequestMethod.GET, produces = "application/json") + public List getUserApps(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List ecompApps = null; + + try { + if (user == null) { + EcompPortalUtils.setBadPermissions(user, response, "getUserApps"); + } else { + ecompApps = appService.transformAppsToEcompApps(appService.getUserApps(user)); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userApps", "GET result =", ecompApps); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getUserApps failed", e); + } + + return ecompApps; + } + + /** + * RESTful service method to fetch all applications accessible to the + * current user, with personalizations. + * + * @return + */ + @RequestMapping(value = { "/portalApi/persUserApps" }, method = RequestMethod.GET, produces = "application/json") + public List getPersUserApps(HttpServletRequest request, HttpServletResponse response) throws IOException { + EPUser user = EPUserUtils.getUserSession(request); + List ecompApps = null; + try { + if (user == null) { + EcompPortalUtils.setBadPermissions(user, response, "getPersUserApps"); + } else { + List apps = null; + if (adminRolesService.isSuperAdmin(user)) + apps = appService.getPersAdminApps(user); + else + apps = appService.getPersUserApps(user); + ecompApps = appService.transformAppsToEcompApps(apps); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userPersApps", "GET result =", ecompApps); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getPersUserApps failed", e); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString()); + } + return ecompApps; + } + + /** + * RESTful service method to fetch applications for which the current user + * is an Administrator + * + * @return + */ + @RequestMapping(value = { "/portalApi/adminApps" }, method = RequestMethod.GET, produces = "application/json") + public List getAdminApps(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List adminApps = null; + + try { + if (!adminRolesService.isAccountAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getAdminApps"); + } else { + adminApps = appService.getAdminApps(user); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/adminApps", "GET result =", adminApps); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAdminApps failed", e); + } + + return adminApps; + } + + /** + * RESTful service method to fetch Applications for user who is super admin + * and/or app admin. + * + * @return + */ + @RequestMapping(value = { + "/portalApi/appsForSuperAdminAndAccountAdmin" }, method = RequestMethod.GET, produces = "application/json") + public List getAppsForSuperAdminAndAccountAdmin(HttpServletRequest request, + HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List adminApps = null; + + try { + if (!adminRolesService.isSuperAdmin(user) && !adminRolesService.isAccountAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getAdminApps"); + } else { + adminApps = appService.getAppsForSuperAdminAndAccountAdmin(user); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/appsForSuperAdminAndAccountAdmin", + "GET result =", adminApps); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAppsForSuperAdminAndAccountAdmin failed", e); + } + + return adminApps; + } + + /** + * RESTful service method to fetch left menu items from the user's session. + * + * @return JSON with left menu + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @RequestMapping(value = { "/portalApi/leftmenuItems" }, method = RequestMethod.GET, produces = "application/json") + public String getLeftMenuItems(HttpServletRequest request, HttpServletResponse response) { + String menuList = null; + Set menuSet = (Set) AppUtils.getSession(request) + .getAttribute(SystemProperties.getProperty(SystemProperties.APPLICATION_MENU_ATTRIBUTE_NAME)); + + Set roleFunctionSet = (Set) AppUtils.getSession(request) + .getAttribute(SystemProperties.getProperty(SystemProperties.ROLE_FUNCTIONS_ATTRIBUTE_NAME)); + + EPUser user = EPUserUtils.getUserSession(request); + + try { + menuList = leftMenuService.getLeftMenuItems(user, menuSet, roleFunctionSet); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/leftmenuItems", "GET result =", menuList); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getLeftMenuItems failed", e); + } + return menuList; + } + + @RequestMapping(value = { + "/portalApi/userAppsOrderBySortPref" }, method = RequestMethod.GET, produces = "application/json") + public List getUserAppsOrderBySortPref(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List ecompApps = null; + try { + if (user == null) { + EcompPortalUtils.setBadPermissions(user, response, "getUserAppsOrderBySortPref"); + } else { + String usrSortPref = request.getParameter("mparams"); + if (usrSortPref.equals("")) { + usrSortPref = "N"; + } + switch (usrSortPref) { + case "N": + ecompApps = appService.transformAppsToEcompApps(appService.getAppsOrderByName(user)); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userAppsOrderBySortPref", "GET result =", + ecompApps); + break; + case "L": + ecompApps = appService.transformAppsToEcompApps(appService.getAppsOrderByLastUsed(user)); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userAppsOrderBySortPref", "GET result =", + ecompApps); + break; + case "F": + ecompApps = appService.transformAppsToEcompApps(appService.getAppsOrderByMostUsed(user)); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userAppsOrderBySortPref", "GET result =", + ecompApps); + break; + case "M": + ecompApps = appService.transformAppsToEcompApps(appService.getAppsOrderByManual(user)); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userAppsOrderBySortPref", "GET result =", + ecompApps); + break; + default: + logger.error(EELFLoggerDelegate.errorLogger, + "getUserAppsOrderBySortPref failed: no match for " + usrSortPref); + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getUserAppsOrderBySortPref failed", e); + } + return ecompApps; + } + + /* + * This method sends the user apps manual sort preference Data to service + * + * @request: HTTP servlet request + * + * @response: HTTP servlet response + */ + @RequestMapping(value = { + "/portalApi/saveUserAppsSortingManual" }, method = RequestMethod.PUT, produces = "application/json") + public FieldsValidator putUserAppsSortingManual(HttpServletRequest request, + @RequestBody List epAppsManualPref, HttpServletResponse response) + throws IOException { + FieldsValidator fieldsValidator = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + fieldsValidator = appService.saveAppsSortManual(epAppsManualPref, user); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "putUserAppsSortingManual failed", e); + } + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/saveUserAppsSortingManual", "PUT result =", + response.getStatus()); + return fieldsValidator; + } + + @RequestMapping(value = { + "/portalApi/saveUserWidgetsSortManual" }, method = RequestMethod.PUT, produces = "application/json") + public FieldsValidator putUserWidgetsSortManual(HttpServletRequest request, + @RequestBody List saveManualWidgetSData, HttpServletResponse response) { + FieldsValidator fieldsValidator = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + fieldsValidator = appService.saveWidgetsSortManual(saveManualWidgetSData, user); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "putUserWidgetsSortManual failed", e); + } + // return fieldsValidator; + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/putUserWidgetsSortManual", "PUT result =", + response.getStatus()); + return fieldsValidator; + } + + @RequestMapping(value = { + "/portalApi/updateWidgetsSortPref" }, method = RequestMethod.PUT, produces = "application/json") + public FieldsValidator putUserWidgetsSortPref(HttpServletRequest request, + @RequestBody List delManualWidgetData, HttpServletResponse response) { + FieldsValidator fieldsValidator = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + fieldsValidator = appService.deleteUserWidgetSortPref(delManualWidgetData, user); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "putUserWidgetsSortPref failed", e); + + } + // return fieldsValidator; + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/putUserWidgetsSortPref", "PUT result =", + response.getStatus()); + return fieldsValidator; + } + + /* + * This method is being called to delete the user app manual sort preference + * record + * + * @request: HTTP servlet request + * + * @response: HTTP servlet response + */ + @RequestMapping(value = { + "/portalApi/UpdateUserAppsSortManual" }, method = RequestMethod.PUT, produces = "application/json") + public FieldsValidator deleteUserAppSortManual(HttpServletRequest request, + @RequestBody EPDeleteAppsManualSortPref delManualAppData, HttpServletResponse response) { + FieldsValidator fieldsValidator = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + fieldsValidator = appService.deleteUserAppSortManual(delManualAppData, user); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "deleteUserAppSortManual failed", e); + + } + // return fieldsValidator; + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/deleteUserAppSortManual", "PUT result =", + response.getStatus()); + return fieldsValidator; + } + + @RequestMapping(value = { + "/portalApi/saveUserAppsSortingPreference" }, method = RequestMethod.PUT, produces = "application/json") + public FieldsValidator putUserAppsSortingPreference(HttpServletRequest request, + @RequestBody EPAppsSortPreference userAppsValue, HttpServletResponse response) { + FieldsValidator fieldsValidator = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + fieldsValidator = appService.saveAppsSortPreference(userAppsValue, user); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "putUserAppsSortingPreference failed", e); + + } + + // return fieldsValidator; + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/putUserAppsSortingPreference", "PUT result =", + response.getStatus()); + return fieldsValidator; + } + + @RequestMapping(value = { + "/portalApi/userAppsSortTypePreference" }, method = RequestMethod.GET, produces = "application/String") + public String getUserAppsSortTypePreference(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + String userSortPreference = null; + + try { + if (user == null) { + EcompPortalUtils.setBadPermissions(user, response, "userAppsSortTypePreference"); + } else { + userSortPreference = appService.getUserAppsSortTypePreference(user); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userAppsSortTypePreference", "GET result =", + userSortPreference); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getUserAppsSortTypePreference failed", e); + } + + return userSortPreference; + } + + /** + * RESTful service method to fetch Application Administrators to Super + * Administrator user. Attention: Users which have Super Administrator roles + * only are not included! + * + * @return + */ + @RequestMapping(value = { "/portalApi/accountAdmins" }, method = RequestMethod.GET, produces = "application/json") + public List getAppsAdministrators(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List admins = null; + try { + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getAppsAdministrators"); + } else { + admins = appService.getAppsAdmins(); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/accountAdmins", "GET result =", admins); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAppsAdministrators failed", e); + } + + return admins; + } + + @RequestMapping(value = { "/portalApi/availableApps" }, method = RequestMethod.GET, produces = "application/json") + public List getApps(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List apps = null; + try { + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getApps"); + } else { + apps = appService.getAllApps(false); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/availableApps", "GET result =", apps); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getApps failed", e); + } + + return apps; + } + + /** + * Gets all apps, both active and inactive; i.e., all on-boarded apps, + * regardless of enabled status. + * + * @param request + * @param response + * @return List of applications + */ + // This API returns + @RequestMapping(value = { + "/portalApi/allAvailableApps" }, method = RequestMethod.GET, produces = "application/json") + public List getAllApps(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List apps = null; + try { + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getApps"); + } else { + apps = appService.getAllApps(true); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/availableApps", "GET result =", apps); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAllApps failed", e); + } + + return apps; + } + + /** + * + * @param request + * @param response + * @return + */ + @RequestMapping(value = { "/portalApi/appsFullList" }, method = RequestMethod.GET, produces = "application/json") + public List getAppsFullList(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List ecompApps = null; + if (user == null) { + EcompPortalUtils.setBadPermissions(user, response, "getAppsFullList"); + } else { + ecompApps = appService.getEcompAppAppsFullList(); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/appsFullList", "GET result =", ecompApps); + } + return ecompApps; + } + + /** + * + * @param request + * @param response + * @return + */ + @RequestMapping(value = { "/portalApi/userProfile" }, method = RequestMethod.GET, produces = "application/json") + public UserRoles getUserProfile(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + UserRoles userAndRoles = null; + try { + if (user == null) { + EcompPortalUtils.setBadPermissions(user, response, "getUserProfile"); + } else { + userAndRoles = appService.getUserProfileNormalized(user); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getUserProfile failed", e); + } + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userProfile", "getUserProfile result =", + userAndRoles); + return userAndRoles; + } + + /** + * + * @param request + * @param appId + * @return + */ + @RequestMapping(value = { "/portalApi/appRoles/{appId}" }, method = { + RequestMethod.GET }, produces = "application/json") + public List getAppRoles(HttpServletRequest request, @PathVariable("appId") Long appId) { + List roleList = null; + try { + roleList = appService.getAppRoles(appId); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/appRoles/" + appId, "GET result =", roleList); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAppRoles failed", e); + } + + return roleList; + } + + /** + * + * @param request + * @param response + * @return + */ + @RequestMapping(value = { "/portalApi/onboardingApps" }, method = RequestMethod.GET, produces = "application/json") + public List getOnboardingApps(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List onboardingApps = null; + try { + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getOnboardingApps"); + } else { + onboardingApps = appService.getOnboardingApps(); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/onboardingApps", "GET result =", + "onboardingApps of size " + onboardingApps.size()); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getOnboardingApps failed", e); + } + + return onboardingApps; + } + + /** + * + * @param request + * @param modifiedOnboardingApp + * @param response + * @return + */ + @RequestMapping(value = { "/portalApi/onboardingApps" }, method = RequestMethod.PUT, produces = "application/json") + public FieldsValidator putOnboardingApp(HttpServletRequest request, + @RequestBody OnboardingApp modifiedOnboardingApp, HttpServletResponse response) { + FieldsValidator fieldsValidator = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "putOnboardingApp"); + } else { + modifiedOnboardingApp.normalize(); + fieldsValidator = appService.modifyOnboardingApp(modifiedOnboardingApp, user); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "putOnboardingApps failed", e); + } + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/onboardingApps", "PUT result =", + response.getStatus()); + return fieldsValidator; + } + + /** + * + * @param request + * @param newOnboardingApp + * @param response + * @return + */ + @RequestMapping(value = { "/portalApi/onboardingApps" }, method = RequestMethod.POST, produces = "application/json") + public FieldsValidator postOnboardingApp(HttpServletRequest request, @RequestBody OnboardingApp newOnboardingApp, + HttpServletResponse response) { + FieldsValidator fieldsValidator = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "postOnboardingApps"); + } else { + newOnboardingApp.normalize(); + fieldsValidator = appService.addOnboardingApp(newOnboardingApp, user); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "postOnboardingApp failed", e); + } + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/onboardingApps", "POST result =", + response.getStatus()); + return fieldsValidator; + } + + /** + * REST endpoint to process a request to delete an on-boarded application. + * + * @param request + * @param appId + * @param response + * @return + */ + @RequestMapping(value = { "/portalApi/onboardingApps/{appId}" }, method = { + RequestMethod.DELETE }, produces = "application/json") + public FieldsValidator deleteOnboardingApp(HttpServletRequest request, @PathVariable("appId") Long appId, + HttpServletResponse response) { + FieldsValidator fieldsValidator = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "deleteOnboardingApps"); + } else { + fieldsValidator = appService.deleteOnboardingApp(user, appId); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "deleteOnboardingApp failed", e); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/onboardingApps" + appId, "DELETE result =", + response.getStatus()); + return fieldsValidator; + } + + /** + * Gets the application thumbnail image; sets status 404 if none exists. + * + * @param request + * HttpServletRequest + * @param appId + * Application ID + * @return Bytes with the app thumbnail image; null if not available. + */ + @RequestMapping(value = { "/portalApi/appThumbnail/{appId}" }, method = { RequestMethod.GET }) + public HttpEntity getAppThumbnail(HttpServletRequest request, @PathVariable("appId") Long appId, + HttpServletResponse response) { + EPApp app = appService.getApp(appId); + if (app == null || app.getImageUrl() == null || app.getThumbnail() == null || app.getThumbnail().length == 0) { + logger.debug(EELFLoggerDelegate.debugLogger, + "getAppThumbnail: no app and/or no thumbnail for app " + appId); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return null; + } + String url = app.getImageUrl(); + int indexOfDot = url.lastIndexOf('.'); + String urlSuffix = indexOfDot > 0 ? url.substring(indexOfDot + 1).toLowerCase() : "UNK"; + // Default to JPG if no usable suffix. + MediaType mediaType = MediaType.IMAGE_JPEG; + if ("png".equals(urlSuffix)) + mediaType = MediaType.IMAGE_PNG; + else if ("gif".equals(urlSuffix)) + mediaType = MediaType.IMAGE_GIF; + HttpHeaders header = new HttpHeaders(); + header.setContentType(mediaType); + header.setContentLength(app.getThumbnail().length); + return new HttpEntity(app.getThumbnail(), header); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppsControllerExternalRequest.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppsControllerExternalRequest.java new file mode 100644 index 00000000..fa49d5b2 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AppsControllerExternalRequest.java @@ -0,0 +1,374 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.AdminRolesService; +import org.openecomp.portalapp.portal.service.EPAppService; +import org.openecomp.portalapp.portal.service.PortalAdminService; +import org.openecomp.portalapp.portal.service.UserService; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.OnboardingApp; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.portal.utils.PortalConstants; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +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.RestController; + +import io.swagger.annotations.ApiOperation; + +/** + * Processes requests from external systems (i.e., not the front-end web UI). + * First use case is ECOMP Controller, which has to create an admin and onboard + * itself upon launch of a fresh Portal. + * + * Listens on the "auxapi" path prefix. Provides alternate implementations of + * methods in several existing controllers because an EPUser object is not + * available in the session for these requests. + * + * Checks credentials sent via HTTP Basic Authentication. The Portal's basic + * HTTP authentication system requires that the user names and endpoints are + * registered together. + */ +@RestController +@RequestMapping(PortalConstants.REST_AUX_API) +@Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class AppsControllerExternalRequest implements BasicAuthenticationController { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AppsControllerExternalRequest.class); + + private static final String ONBOARD_APP = "/onboardApp"; + + // Where is this used? + public boolean isAuxRESTfulCall() { + return true; + } + + /** + * For testing whether a user is a superadmin. + */ + @Autowired + private AdminRolesService adminRolesService; + + /** + * For onboarding or updating an app + */ + @Autowired + private EPAppService appService; + + /** + * For promoting a user to Portal admin + */ + @Autowired + private PortalAdminService portalAdminService; + + /** + * For creating a new user + */ + @Autowired + private UserService userService; + + /** + * Creates a new user as a Portal administrator. + * + *
+	 { 
+		"loginId" : "abc123",
+		"loginPwd": "",
+		"email":"ecomp@controller" 
+	 }
+	 * 
+ * + * @param request + * HttpServletRequest + * @param epUser + * User details; the email and orgUserId fields are mandatory + * @param response + * HttpServletResponse + * @return PortalRestResponse with success or failure + */ + @ApiOperation(value = "Creates a new user as a Portal administrator.", response = PortalRestResponse.class) + @RequestMapping(value = "/portalAdmin", method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public PortalRestResponse postPortalAdmin(HttpServletRequest request, HttpServletResponse response, + @RequestBody EPUser epUser) { + EcompPortalUtils.logAndSerializeObject(logger, "postPortalAdmin", "request", epUser); + PortalRestResponse portalResponse = new PortalRestResponse<>(); + + // Check mandatory fields. + if (epUser.getEmail() == null || epUser.getEmail().trim().length() == 0 // + || epUser.getLoginId() == null || epUser.getLoginId().trim().length() == 0 // + || epUser.getLoginPwd() == null) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage("Missing required field: email, loginId, or loginPwd"); + return portalResponse; + } + + try { + // Check for existing user; create if not found. + List userList = userService.getUserByUserId(epUser.getOrgUserId()); + if (userList == null || userList.size() == 0) { + // Create user with first, last names etc.; do check for + // duplicates. + String userCreateResult = userService.saveNewUser(epUser, "Yes"); + if (!"success".equals(userCreateResult)) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage(userCreateResult); + return portalResponse; + } + } + + // Check for Portal admin status; promote if not. + if (adminRolesService.isSuperAdmin(epUser)) { + portalResponse.setStatus(PortalRestStatusEnum.OK); + } else { + FieldsValidator fv = portalAdminService.createPortalAdmin(epUser.getOrgUserId()); + if (fv.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + portalResponse.setStatus(PortalRestStatusEnum.OK); + } else { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage(fv.toString()); + } + } + } catch (Exception ex) { + // Uncaught exceptions yield 404 and an empty error page + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage(ex.toString()); + } + + EcompPortalUtils.logAndSerializeObject(logger, "postPortalAdmin", "response", portalResponse); + return portalResponse; + } + + /** + * Gets the specified application that is on-boarded in Portal. + * + * @param request + * HttpServletRequest + * @param appId + * Application ID to get + * @param response + * httpServletResponse + * @return OnboardingApp objects + */ + @ApiOperation(value = "Gets the specified application that is on-boarded in Portal.", response = OnboardingApp.class) + @RequestMapping(value = { ONBOARD_APP + "/{appId}" }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public OnboardingApp getOnboardAppExternal(HttpServletRequest request, HttpServletResponse response, + @PathVariable("appId") Long appId) { + EPApp epApp = appService.getApp(appId); + OnboardingApp obApp = new OnboardingApp(); + appService.createOnboardingFromApp(epApp, obApp); + EcompPortalUtils.logAndSerializeObject(logger, "getOnboardAppExternal", "response", obApp); + return obApp; + } + + /** + * Adds a new application to Portal. The My Logins App Owner in the request + * must be the organization user ID of a person who is a Portal + * administrator. + * + *
+	 * { 
+		"myLoginsAppOwner" : "abc123",
+		"name": "dashboard",
+		"url": "http://k8s/something",
+		"restUrl" : "http://aic.att.com",
+		"restrictedApp" : true,
+		"isOpen" : true,
+		"isEnabled": false
+		}
+	 * 
+ * + * @param request + * HttpServletRequest + * @param response + * httpServletResponse + * @param newOnboardApp + * Message with details about the app to add + * @return PortalRestResponse + */ + @ApiOperation(value = "Adds a new application to Portal.", response = PortalRestResponse.class) + @RequestMapping(value = { ONBOARD_APP }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public PortalRestResponse postOnboardAppExternal(HttpServletRequest request, HttpServletResponse response, + @RequestBody OnboardingApp newOnboardApp) { + EcompPortalUtils.logAndSerializeObject(logger, "postOnboardAppExternal", "request", newOnboardApp); + PortalRestResponse portalResponse = new PortalRestResponse<>(); + + // Validate fields + if (newOnboardApp.id != null) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage("Unexpected field: id"); + return portalResponse; + } + if (newOnboardApp.name == null || newOnboardApp.name.trim().length() == 0 // + || newOnboardApp.url == null || newOnboardApp.url.trim().length() == 0 // + || newOnboardApp.restUrl == null || newOnboardApp.restUrl.trim().length() == 0 + || newOnboardApp.myLoginsAppOwner == null || newOnboardApp.myLoginsAppOwner.trim().length() == 0 + || newOnboardApp.restrictedApp == null // + || newOnboardApp.isOpen == null // + || newOnboardApp.isEnabled == null) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage( + "Missing required field: name, url, restUrl, restrictedApp, isOpen, isEnabled, myLoginsAppOwner"); + return portalResponse; + } + + try { + List userList = userService.getUserByUserId(newOnboardApp.myLoginsAppOwner); + if (userList == null || userList.size() != 1) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage("Failed to find user: " + newOnboardApp.myLoginsAppOwner); + return portalResponse; + } + + EPUser epUser = userList.get(0); + // Check for Portal admin status + if (! adminRolesService.isSuperAdmin(epUser)) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage("User lacks Portal admin role: " + epUser.getLoginId()); + return portalResponse; + } + + newOnboardApp.normalize(); + FieldsValidator fv = appService.addOnboardingApp(newOnboardApp, epUser); + if (fv.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + portalResponse.setStatus(PortalRestStatusEnum.OK); + } else { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage(fv.toString()); + } + } catch (Exception ex) { + // Uncaught exceptions yield 404 and an empty error page + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage(ex.toString()); + } + EcompPortalUtils.logAndSerializeObject(logger, "postOnboardAppExternal", "response", portalResponse); + return portalResponse; + } + + /** + * Updates information about an on-boarded application in Portal. The My + * Logins App Owner in the request must be the organization user ID of a + * person who is a Portal administrator. + *
+	   { 
+		"id" : 123,
+		"myLoginsAppOwner" : "abc123",
+		"name": "dashboard",
+		"url": "http://k8s/something",
+		"restUrl" : "http://aic.att.com",
+		"restrictedApp" : true,
+		"isOpen" : true,
+		"isEnabled": false
+		}
+		
+ * @param request + * HttpServletRequest + * @param response + * httpServletResponse + * @param appId + * application id + * @param oldOnboardApp + * Message with details about the app to add + * @return PortalRestResponse + */ + @ApiOperation(value = "Updates information about an on-boarded application in Portal.", response = PortalRestResponse.class) + @RequestMapping(value = { ONBOARD_APP + "/{appId}" }, method = RequestMethod.PUT, produces = "application/json") + @ResponseBody + public PortalRestResponse putOnboardAppExternal(HttpServletRequest request, HttpServletResponse response, + @PathVariable("appId") Long appId, @RequestBody OnboardingApp oldOnboardApp) { + EcompPortalUtils.logAndSerializeObject(logger, "putOnboardAppExternal", "request", oldOnboardApp); + PortalRestResponse portalResponse = new PortalRestResponse<>(); + // Validate fields. + if (oldOnboardApp.id == null || !appId.equals(oldOnboardApp.id)) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage("Unexpected value for field: id"); + return portalResponse; + } + if (oldOnboardApp.name == null || oldOnboardApp.name.trim().length() == 0 // + || oldOnboardApp.url == null || oldOnboardApp.url.trim().length() == 0 // + || oldOnboardApp.restUrl == null || oldOnboardApp.restUrl.trim().length() == 0 + || oldOnboardApp.myLoginsAppOwner == null || oldOnboardApp.myLoginsAppOwner.trim().length() == 0 + || oldOnboardApp.restrictedApp == null // + || oldOnboardApp.isOpen == null // + || oldOnboardApp.isEnabled == null) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage( + "Missing required field: name, url, restUrl, restrictedApp, isOpen, isEnabled, myLoginsAppOwner"); + return portalResponse; + } + + try { + List userList = userService.getUserByUserId(oldOnboardApp.myLoginsAppOwner); + if (userList == null || userList.size() != 1) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage("Failed to find user: " + oldOnboardApp.myLoginsAppOwner); + return portalResponse; + } + + EPUser epUser = userList.get(0); + // Check for Portal admin status + if (! adminRolesService.isSuperAdmin(epUser)) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage("User lacks Portal admin role: " + epUser.getLoginId()); + return portalResponse; + } + + oldOnboardApp.normalize(); + FieldsValidator fv = appService.modifyOnboardingApp(oldOnboardApp, epUser); + if (fv.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + portalResponse.setStatus(PortalRestStatusEnum.OK); + } else { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage(fv.toString()); + } + } catch (Exception ex) { + // Uncaught exceptions yield 404 and an empty error page + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage(ex.toString()); + } + EcompPortalUtils.logAndSerializeObject(logger, "putOnboardAppExternal", "response", portalResponse); + return portalResponse; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AuditLogController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AuditLogController.java new file mode 100644 index 00000000..705c9008 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/AuditLogController.java @@ -0,0 +1,106 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import org.openecomp.portalsdk.core.domain.AuditLog; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.AuditService; +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.aop.EPEELFLoggerAdvice; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.util.EPUserUtils; + +@RestController +@RequestMapping("/portalApi/auditLog") +public class AuditLogController extends EPRestrictedBaseController { + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(DashboardController.class); + + @Autowired + private AuditService auditService; + + /** + * Store audit log of the specified access type. + * + * @param request + * @param affectedAppId + * @param type + * @param comment + */ + @RequestMapping(value = "/store", method = RequestMethod.GET, produces = "application/json") + public void auditLog(HttpServletRequest request, @RequestParam String affectedAppId, @RequestParam String type, + @RequestParam String comment) { + logger.debug(EELFLoggerDelegate.debugLogger, "auditLog: appId {}, type {], comment {}", + affectedAppId, type, comment); + String cd_type = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + /* Check type of Activity CD */ + if (type.equals("app")) { + cd_type = AuditLog.CD_ACTIVITY_APP_ACCESS; + } else if (type.equals("tab")) { + cd_type = AuditLog.CD_ACTIVITY_TAB_ACCESS; + } else if (type.equals("functional")) { + cd_type = AuditLog.CD_ACTIVITY_FUNCTIONAL_ACCESS; + } else if (type.equals("leftMenu")) { + cd_type = AuditLog.CD_ACTIVITY_LEFT_MENU_ACCESS; + } else { + logger.error(EELFLoggerDelegate.errorLogger, "Storing auditLog failed! Activity CD type is not correct."); + } + /* Store the audit log only if it contains valid Activity CD */ + if (cd_type != null) { + AuditLog auditLog = new AuditLog(); + auditLog.setActivityCode(cd_type); + /* + * Check affectedAppId and comment and see if these two values are + * valid + */ + if (comment != null && !comment.equals("") && !comment.equals("undefined")) + auditLog.setComments(comment); + if (affectedAppId != null && !affectedAppId.equals("") && !affectedAppId.equals("undefined")) + auditLog.setAffectedRecordId(affectedAppId); + long userId = EPUserUtils.getUserId(request); + auditLog.setUserId(userId); + auditService.logActivity(auditLog, null); + + // Log file + MDC.put(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP,EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + MDC.put(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP,EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + logger.info(EELFLoggerDelegate.auditLogger, EPLogUtil.formatAuditLogMessage("AuditLogController.auditLog", + cd_type, user.getOrgUserId(), affectedAppId, comment)); + MDC.remove(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "auditLog failed", e); + } + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BEPropertyReaderController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BEPropertyReaderController.java new file mode 100644 index 00000000..a7b3f1a1 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BEPropertyReaderController.java @@ -0,0 +1,46 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import org.openecomp.portalsdk.core.controller.FusionBaseController; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalapp.portal.domain.BEProperty; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; + +@RestController +@RequestMapping("/portalApi/properties") +public class BEPropertyReaderController extends FusionBaseController{ + @RequestMapping(value = "/readProperty", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse readProperty(HttpServletRequest request, @RequestParam String key) { + try { + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", new BEProperty(key, SystemProperties.getProperty(key))); + } catch (Exception e) { + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.toString(), null); + } + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BasicAuthAccountController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BasicAuthAccountController.java new file mode 100644 index 00000000..f23054bd --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BasicAuthAccountController.java @@ -0,0 +1,141 @@ +package org.openecomp.portalapp.portal.controller; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.BasicAuthCredentials; +import org.openecomp.portalapp.portal.domain.EPEndpoint; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.AdminRolesService; +import org.openecomp.portalapp.portal.service.BasicAuthAccountService; +import org.openecomp.portalapp.util.EPUserUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@SuppressWarnings("unchecked") +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class BasicAuthAccountController extends EPRestrictedBaseController{ + + @Autowired + private BasicAuthAccountService basicAuthAccountService; + + @Autowired + private AdminRolesService adminRolesService; + + /** + * Saves Basic Authentication account for external systems + * @param BasicAuthCredentials + * @return Id of the newly created account + */ + + @RequestMapping(value = { "/portalApi/basicAuthAccount" }, method = RequestMethod.POST) + public PortalRestResponse createBasicAuthAccount(HttpServletRequest request, HttpServletResponse response, + @RequestBody BasicAuthCredentials newBasicAuthAccount) throws Exception { + + EPUser user = EPUserUtils.getUserSession(request); + if (!adminRolesService.isSuperAdmin(user)){ + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "Authorization Required", "Admin Only Operation! "); + } + + if(newBasicAuthAccount == null){ + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", + "newBasicAuthAccount cannot be null or empty"); + } + long accountId = basicAuthAccountService.saveBasicAuthAccount(newBasicAuthAccount); + + List endpointIdList = new ArrayList<>(); + try { + for(EPEndpoint ep: newBasicAuthAccount.getEndpoints()){ + endpointIdList.add(basicAuthAccountService.saveEndpoints(ep)); + } + for(Long endpointId: endpointIdList){ + basicAuthAccountService.saveEndpointAccount(accountId, endpointId); + } + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", e.getMessage()); + } + + return new PortalRestResponse(PortalRestStatusEnum.OK, "SUCCESS", ""); + } + + /** + * Returns list of all BasicAuthCredentials in the system + * @return List + */ + + @RequestMapping(value = { "/portalApi/basicAuthAccount" }, method = RequestMethod.GET) + public PortalRestResponse> getBasicAuthAccount(HttpServletRequest request, HttpServletResponse response) + throws Exception { + + EPUser user = EPUserUtils.getUserSession(request); + if (!adminRolesService.isSuperAdmin(user)){ + return new PortalRestResponse>(PortalRestStatusEnum.ERROR, "UnAuthorized! Admin Only Operation", new ArrayList<>()); + } + + return new PortalRestResponse>(PortalRestStatusEnum.OK, "Success", basicAuthAccountService.getAccountData()); + } + + /** + * Updates an existing BasicAuthCredentials account + */ + + @RequestMapping(value = { "/portalApi/basicAuthAccount/{accountId}" }, method = RequestMethod.PUT) + public PortalRestResponse updateAccount(HttpServletRequest request, HttpServletResponse response, + @PathVariable("accountId") long accountId, @RequestBody BasicAuthCredentials newBasicAuthAccount) throws Exception { + + EPUser user = EPUserUtils.getUserSession(request); + if (!adminRolesService.isSuperAdmin(user)){ + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "Authorization Required", "Admin Only Operation! "); + } + + if (newBasicAuthAccount == null) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", + "BasicAuthCredentials cannot be null or empty"); + } + try { + basicAuthAccountService.updateBasicAuthAccount(accountId, newBasicAuthAccount); + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.OK, "SUCCESS", ""); + } + + /** + * deletes an existing BasicAuthCredentials account + */ + + @RequestMapping(value = { "/portalApi/basicAuthAccount/{accountId}" }, method = RequestMethod.DELETE) + public PortalRestResponse deleteAccount(HttpServletRequest request, HttpServletResponse response, + @PathVariable("accountId") long accountId) throws Exception { + + EPUser user = EPUserUtils.getUserSession(request); + if (!adminRolesService.isSuperAdmin(user)){ + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "Authorization Required", "Admin Only Operation! "); + } + + + try { + basicAuthAccountService.deleteEndpointAccout(accountId); + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.OK, "SUCCESS", ""); + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BasicAuthenticationController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BasicAuthenticationController.java new file mode 100644 index 00000000..d9bdf3dc --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/BasicAuthenticationController.java @@ -0,0 +1,28 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +/** + * Marker interface to indicate that requests to this controller present HTTP + * Basic Authentication credentials. + */ +public interface BasicAuthenticationController { + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/CommonWidgetController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/CommonWidgetController.java new file mode 100644 index 00000000..68e32a16 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/CommonWidgetController.java @@ -0,0 +1,79 @@ +/* + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import javax.servlet.http.HttpServletRequest; +import org.openecomp.portalapp.portal.controller.DashboardController.WidgetCategory; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.service.DashboardSearchService; +import org.openecomp.portalapp.portal.transport.CommonWidgetMeta; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CommonWidgetController implements BasicAuthenticationController{ + + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CommonWidgetController.class); + @Autowired + private DashboardSearchService searchService; + + /** + * Validates the resource type parameter. + * + * @param resourceType + * @return True if known in the enum WidgetCategory, else false. + */ + private boolean isValidResourceType(String resourceType) { + if (resourceType == null) + return false; + for (WidgetCategory wc : WidgetCategory.values()) + if (wc.name().equals(resourceType)) + return true; + return false; + } + + /** + * Gets all widgets of the specified resource type. + * + * @param request + * @param resourceType + * Request parameter. + * @return Rest response wrapped around a CommonWidgetMeta object. + */ + @RequestMapping(value = "/widgetData", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse getWidgetData(HttpServletRequest request, + @RequestParam String resourceType) { + if (!isValidResourceType(resourceType)){ + logger.debug(EELFLoggerDelegate.debugLogger, "Unexpected resource type {}", resourceType); + return new PortalRestResponse(PortalRestStatusEnum.ERROR, + "Unexpected resource type " + resourceType, null); + } + return new PortalRestResponse(PortalRestStatusEnum.OK, "success", + searchService.getWidgetData(resourceType)); + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ConsulClientController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ConsulClientController.java new file mode 100644 index 00000000..8db94849 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ConsulClientController.java @@ -0,0 +1,90 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.service.ConsulHealthService; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import com.orbitz.consul.ConsulException; +import com.orbitz.consul.model.health.ServiceHealth; + +import io.searchbox.client.config.exception.NoServerConfiguredException; + +@RestController +@RequestMapping("/portalApi/consul") +public class ConsulClientController extends EPRestrictedBaseController { + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ConsulClientController.class); + + @Autowired + private ConsulHealthService consulHealthService; + //Get location of a healthy node running our service + @RequestMapping(value = { "/service/{service}" }, method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse getServiceLocation(HttpServletRequest request, HttpServletResponse response, @PathVariable("service") String service) { + + try{ + return new PortalRestResponse(PortalRestStatusEnum.OK, "Success!", consulHealthService.getServiceLocation(service)); + } + catch(NoServerConfiguredException e){ + logger.error(logger.errorLogger, "No healthy service exception!"); + return new PortalRestResponse(PortalRestStatusEnum.WARN, "Warning!", "No healthy service exception!"); + } + catch(ConsulException e){ + logger.error(logger.errorLogger, "Couldn't connect ot consul - Is consul running?"); + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "Error!", "Couldn't connect ot consul - Is consul running?"); + } + } + + @RequestMapping(value = { "/service/healthy/{service}" }, method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getAllHealthyNodes(HttpServletRequest request, HttpServletResponse response, @PathVariable("service") String service) { + try{ + return new PortalRestResponse>(PortalRestStatusEnum.OK, "Success!", consulHealthService.getAllHealthyNodes(service)); + } + catch(ConsulException e){ + logger.error(logger.errorLogger, "Couldn't connect to consul - shouldn't break anything."); + return new PortalRestResponse>(PortalRestStatusEnum.ERROR,"Error!", new ArrayList<>()); + } + } + + @RequestMapping(value = { "/service/all/{service}" }, method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getAllNodes(HttpServletRequest request, HttpServletResponse response, @PathVariable("service") String service) { + try{ + return new PortalRestResponse>(PortalRestStatusEnum.OK, "Success!", consulHealthService.getAllNodes(service)); + } + catch(ConsulException e){ + logger.error(logger.errorLogger, "Couldn't connect to consul - shouldn't break anything."); + return new PortalRestResponse>(PortalRestStatusEnum.ERROR,"Error!", new ArrayList<>()); + } + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/DashboardController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/DashboardController.java new file mode 100644 index 00000000..290a5ffc --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/DashboardController.java @@ -0,0 +1,385 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EcompAuditLog; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.ecomp.model.SearchResultItem; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.logging.aop.EPEELFLoggerAdvice; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.service.DashboardSearchService; +import org.openecomp.portalapp.portal.transport.CommonWidget; +import org.openecomp.portalapp.portal.transport.CommonWidgetMeta; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.util.EPUserUtils; +import org.openecomp.portalsdk.core.domain.AuditLog; +import org.openecomp.portalsdk.core.domain.support.CollaborateList; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.AuditService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * Controller supplies data to Angular services on the dashboard page. + */ +@Configuration +@RestController +@RequestMapping("/portalApi/dashboard") +public class DashboardController extends EPRestrictedBaseController { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(DashboardController.class); + + @Autowired + private DashboardSearchService searchService; + @Autowired + private AuditService auditService; + + public enum WidgetCategory { + EVENTS, NEWS, IMPORTANTRESOURCES; + } + + /** + * Validates the resource type parameter. + * + * @param resourceType + * @return True if known in the enum WidgetCategory, else false. + */ + private boolean isValidResourceType(String resourceType) { + if (resourceType == null) + return false; + for (WidgetCategory wc : WidgetCategory.values()) + if (wc.name().equals(resourceType)) + return true; + return false; + } + + /** + * Gets all widgets of the specified resource type. + * In iteration 41 (when widget will utilized service onboarding), this method can be removed, instead we will use CommonWidgetController.java (basic auth based) + * + * @param request + * @param resourceType + * Request parameter. + * @return Rest response wrapped around a CommonWidgetMeta object. + */ + @RequestMapping(value = "/widgetData", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse getWidgetData(HttpServletRequest request, + @RequestParam String resourceType) { + if (!isValidResourceType(resourceType)) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, + "Unexpected resource type " + resourceType, null); + return new PortalRestResponse(PortalRestStatusEnum.OK, "success", + searchService.getWidgetData(resourceType)); + } + + + /** + * Saves a batch of events, news or resources. + * + * @param commonWidgetMeta + * read from POST body. + * @return Rest response wrapped around a String; e.g., "success" or "ERROR" + */ + @RequestMapping(value = "/widgetDataBulk", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse saveWidgetDataBulk(@RequestBody CommonWidgetMeta commonWidgetMeta) { + logger.debug(EELFLoggerDelegate.debugLogger, "saveWidgetDataBulk: argument is {}", commonWidgetMeta); + if (commonWidgetMeta.getCategory() == null || commonWidgetMeta.getCategory().trim().equals("")) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "ERROR", + "Category cannot be null or empty"); + if (!isValidResourceType(commonWidgetMeta.getCategory())) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, + "Unexpected resource type " + commonWidgetMeta.getCategory(), null); + // validate dates + for (CommonWidget cw : commonWidgetMeta.getItems()) { + String err = validateCommonWidget(cw); + if (err != null) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, err, null); + } + return new PortalRestResponse(PortalRestStatusEnum.OK, "success", + searchService.saveWidgetDataBulk(commonWidgetMeta)); + } + + /** + * Saves one: event, news or resource + * + * @param commonWidget + * read from POST body + * @return Rest response wrapped around a String; e.g., "success" or "ERROR" + */ + @RequestMapping(value = "/widgetData", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse saveWidgetData(@RequestBody CommonWidget commonWidget) { + logger.debug(EELFLoggerDelegate.debugLogger, "saveWidgetData: argument is {}", commonWidget); + if (commonWidget.getCategory() == null || commonWidget.getCategory().trim().equals("")) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "ERROR", + "Category cannot be null or empty"); + String err = validateCommonWidget(commonWidget); + if (err != null) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, err, null); + return new PortalRestResponse(PortalRestStatusEnum.OK, "success", + searchService.saveWidgetData(commonWidget)); + } + + /** + * Used by the validate function + */ + private final SimpleDateFormat yearMonthDayFormat = new SimpleDateFormat("yyyy-MM-dd"); + + /** + * Validates the content of a common widget. + * + * @param cw + * @return null on success; an error message if validation fails. + * @throws Exception + */ + private String validateCommonWidget(CommonWidget cw) { + if (!isValidResourceType(cw.getCategory())) + return "Invalid category: " + cw.getCategory(); + if (cw.getTitle() == null || cw.getTitle().trim().length() == 0) + return "Title is missing"; + if (cw.getHref() == null || cw.getHref().trim().length() == 0) + return "HREF is missing"; + if (!cw.getHref().toLowerCase().startsWith("http")) + return "HREF does not start with http"; + if (cw.getSortOrder() == null) + return "Sort order is null"; + if (WidgetCategory.EVENTS.name().equals(cw.getCategory())) { + if (cw.getEventDate() == null || cw.getEventDate().trim().length() == 0) + return "Date is missing"; + try { + yearMonthDayFormat.setLenient(false); + Date date = yearMonthDayFormat.parse(cw.getEventDate()); + if (date == null) + return "Failed to parse date " + cw.getEventDate(); + } catch (ParseException ex) { + return ex.toString(); + } + } + return null; + } + + /** + * Deletes one: event, news or resource + * + * @param commonWidget + * read from POST body + * @return Rest response wrapped around a String; e.g., "success" or "ERROR" + */ + @RequestMapping(value = "/deleteData", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse deleteWidgetData(@RequestBody CommonWidget commonWidget) { + logger.debug(EELFLoggerDelegate.debugLogger, "deleteWidgetData: argument is {}", commonWidget); + return new PortalRestResponse(PortalRestStatusEnum.OK, "success", + searchService.deleteWidgetData(commonWidget)); + } + + /** + * Searches all portal for the input string. + * + * @param request + * @param searchString + * @return Rest response wrapped around a Map of String to List of Search + * Result Item. + */ + @EPAuditLog + @RequestMapping(value = "/search", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse>> searchPortal(HttpServletRequest request, + @RequestParam String searchString) { + + if (searchString != null) + searchString = searchString.trim(); + EPUser user = EPUserUtils.getUserSession(request); + try { + if (user == null) { + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, + "searchPortal: User object is null? - check logs", + new HashMap>()); + } else if (searchString == null || searchString.length() == 0) { + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, "searchPortal: String string is null", + new HashMap>()); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "searchPortal: user {}, search string '{}'", + user.getLoginId(), searchString); + Map> results = searchService.searchResults(user.getLoginId(), + searchString); + /*Audit log the search*/ + AuditLog auditLog = new AuditLog(); + auditLog.setUserId(user.getId()); + auditLog.setActivityCode(EcompAuditLog.CD_ACTIVITY_SEARCH); + auditLog.setComments(searchString); + MDC.put(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP,EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + auditService.logActivity(auditLog, null); + MDC.put(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP,EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + EcompPortalUtils.calculateDateTimeDifferenceForLog(MDC.get(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP),MDC.get(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP)); + logger.info(EELFLoggerDelegate.auditLogger, EPLogUtil.formatAuditLogMessage("DashboardController.PortalRestResponse", + EcompAuditLog.CD_ACTIVITY_SEARCH, user.getOrgUserId(), null, searchString)); + MDC.remove(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP); + MDC.remove(SystemProperties.MDC_TIMER); + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", results); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "searchPortal failed", e); + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.getMessage() + " - check logs.", + new HashMap>()); + } + } + + /** + * Gets all active users. + * + * TODO: should only the superuser be allowed to use this API? + * + * @param request + * @return Rest response wrapped around a list of String + */ + @RequestMapping(value = "/activeUsers", method = RequestMethod.GET, produces = "application/json") + public List getActiveUsers(HttpServletRequest request) { + List activeUsers = null; + List onlineUsers = new ArrayList<>(); + try { + EPUser user = EPUserUtils.getUserSession(request); + String userId = user.getOrgUserId(); + + activeUsers = searchService.getRelatedUsers(userId); + HashSet usersSet = CollaborateList.getInstance().getAllUserName(); + for (String users : activeUsers) { + if (usersSet.contains(users)) { + onlineUsers.add(users); + } + } + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getActiveUsers failed", e); + } + return onlineUsers; + } + + /** + * Gets the refresh interval and duration of a cycle of continuous refreshing for the online users side panel, both in milliseconds. + * + * @param request + * @return Rest response wrapped around a number that is the number of milliseconds. + */ + @RequestMapping(value = "/onlineUserUpdateRate", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getOnlineUserUpdateRate(HttpServletRequest request) { + try { + String updateRate = SystemProperties.getProperty(EPCommonSystemProperties.ONLINE_USER_UPDATE_RATE); + String updateDuration = SystemProperties.getProperty(EPCommonSystemProperties.ONLINE_USER_UPDATE_DURATION); + Integer rateInMiliSec = Integer.valueOf(updateRate)*1000; + Integer durationInMiliSec = Integer.valueOf(updateDuration)*1000; + Map results = new HashMap(); + results.put("onlineUserUpdateRate", String.valueOf(rateInMiliSec)); + results.put("onlineUserUpdateDuration", String.valueOf(durationInMiliSec)); + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", results); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getOnlineUserUpdateRate failed", e); + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.toString(), null); + } + } + + /** + * Gets the window width threshold for collapsing right menu from system.properties. + * + * @param request + * @return Rest response wrapped around a number that is the window width threshold to collapse right menu. + */ + @RequestMapping(value = "/windowWidthThresholdRightMenu", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getWindowWidthThresholdForRightMenu(HttpServletRequest request) { + try { + String windowWidthString = SystemProperties.getProperty(EPCommonSystemProperties.WINDOW_WIDTH_THRESHOLD_RIGHT_MENU); + Integer windowWidth = Integer.valueOf(windowWidthString); + Map results = new HashMap(); + results.put("windowWidth", String.valueOf(windowWidth)); + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", results); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getWindowWidthThresholdForRightMenu failed", e); + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.toString(), null); + } + } + + + /** + * Gets the window width threshold for collapsing left menu from system.properties. + * + * @param request + * @return Rest response wrapped around a number that is the window width threshold to collapse the left menu. + */ + @RequestMapping(value = "/windowWidthThresholdLeftMenu", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getWindowWidthThresholdForLeftMenu(HttpServletRequest request) { + try { + String windowWidthString = SystemProperties.getProperty(EPCommonSystemProperties.WINDOW_WIDTH_THRESHOLD_LEFT_MENU); + Integer windowWidth = Integer.valueOf(windowWidthString); + Map results = new HashMap(); + results.put("windowWidth", String.valueOf(windowWidth)); + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", results); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getWindowWidthThresholdForLeftMenu failed", e); + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.toString(), null); + } + } + + /** + * Gets only those users that are 'related' to the currently logged-in user. + * + * @param request + * @return Rest response wrapped around a List of String + */ + @RequestMapping(value = "/relatedUsers", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> activeUsers(HttpServletRequest request) { + EPUser user = EPUserUtils.getUserSession(request); + try { + if (user == null) { + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, "User object is null? - check logs", + new ArrayList<>()); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "activeUsers: searching for user {}", user.getLoginId()); + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", + searchService.getRelatedUsers(user.getLoginId())); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "activeUsers failed", e); + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.getMessage() + " - check logs.", + new ArrayList<>()); + } + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/DashboardSearchResultController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/DashboardSearchResultController.java new file mode 100644 index 00000000..fe31066d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/DashboardSearchResultController.java @@ -0,0 +1,243 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.ecomp.model.SearchResultItem; +import org.openecomp.portalapp.portal.service.DashboardSearchService; +import org.openecomp.portalapp.portal.transport.CommonWidget; +import org.openecomp.portalapp.portal.transport.CommonWidgetMeta; +import org.openecomp.portalapp.util.EPUserUtils; +import org.openecomp.portalsdk.core.domain.support.CollaborateList; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/portalApi/search") +public class DashboardSearchResultController extends EPRestrictedBaseController { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(DashboardSearchResultController.class); + + @Autowired + private DashboardSearchService searchService; + + /** + * Gets all widgets by type: NEW or RESOURCE + * + * @param request + * @param resourceType + * Request parameter. + * @return Rest response wrapped around a CommonWidgetMeta object. + */ + @RequestMapping(value = "/widgetData", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse getWidgetData(HttpServletRequest request, + @RequestParam String resourceType) { + return new PortalRestResponse(PortalRestStatusEnum.OK, "success", + searchService.getWidgetData(resourceType)); + } + + /** + * Saves all: news and resources + * + * @param commonWidgetMeta + * read from POST body. + * @return Rest response wrapped around a String; e.g., "success" or "ERROR" + */ + @RequestMapping(value = "/widgetDataBulk", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse saveWidgetDataBulk(@RequestBody CommonWidgetMeta commonWidgetMeta) { + logger.debug(EELFLoggerDelegate.debugLogger, "saveWidgetDataBulk: argument is {}", commonWidgetMeta); + if (commonWidgetMeta.getCategory() == null || commonWidgetMeta.getCategory().trim().equals("")) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "ERROR", + "Category cannot be null or empty"); + // validate dates + for (CommonWidget cw : commonWidgetMeta.getItems()) { + String err = validateCommonWidget(cw); + if (err != null) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, err, null); + } + return new PortalRestResponse(PortalRestStatusEnum.OK, "success", + searchService.saveWidgetDataBulk(commonWidgetMeta)); + } + + /** + * Saves one: news or resource + * + * @param commonWidget + * read from POST body + * @return Rest response wrapped around a String; e.g., "success" or "ERROR" + */ + @RequestMapping(value = "/widgetData", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse saveWidgetData(@RequestBody CommonWidget commonWidget) { + logger.debug(EELFLoggerDelegate.debugLogger, "saveWidgetData: argument is {}", commonWidget); + if (commonWidget.getCategory() == null || commonWidget.getCategory().trim().equals("")) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "ERROR", + "Cateogry cannot be null or empty"); + String err = validateCommonWidget(commonWidget); + if (err != null) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, err, null); + return new PortalRestResponse(PortalRestStatusEnum.OK, "success", + searchService.saveWidgetData(commonWidget)); + } + + /** + * Used by the validate function + */ + private final SimpleDateFormat yearMonthDayFormat = new SimpleDateFormat("yyyy-MM-dd"); + + /** + * Validates the content of a common widget. + * + * @param cw + * @return null on success; an error message if validation fails. + * @throws Exception + */ + private String validateCommonWidget(CommonWidget cw) { + try { + if (cw.getEventDate() != null && cw.getEventDate().trim().length() > 0) + yearMonthDayFormat.parse(cw.getEventDate()); + } catch (ParseException ex) { + return ex.toString(); + } + return null; + } + + /** + * Deletes one: news or resource + * + * @param commonWidget + * read from POST body + * @return Rest response wrapped around a String; e.g., "success" or "ERROR" + */ + @RequestMapping(value = "/deleteData", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse deleteWidgetData(@RequestBody CommonWidget commonWidget) { + logger.debug(EELFLoggerDelegate.debugLogger, "deleteWidgetData: argument is {}", commonWidget); + return new PortalRestResponse(PortalRestStatusEnum.OK, "success", + searchService.deleteWidgetData(commonWidget)); + } + + /** + * Searches all portal for the input string. + * + * @param request + * @param searchString + * @return Rest response wrapped around a Map of String to List of Search + * Result Item. + */ + @RequestMapping(value = "/allPortal", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse>> searchPortal(HttpServletRequest request, + @RequestParam String searchString) { + + EPUser user = EPUserUtils.getUserSession(request); + try { + if (user == null) { + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, + "searchPortal: User object is null? - check logs", + new HashMap>()); + } else if (searchString == null || searchString.trim().length() == 0) { + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, "searchPortal: String string is null", + new HashMap>()); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "searchPortal: user {}, search string '{}'", + user.getLoginId(), searchString); + Map> results = searchService.searchResults(user.getLoginId(), + searchString); + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", results); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "searchPortal failed", e); + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.getMessage() + " - check logs.", + new HashMap>()); + } + } + + /** + * Gets all active users. + * + * TODO: should only the superuser be allowed to use this API? + * + * @param request + * @return Rest response wrapped around a list of String + */ + @RequestMapping(value = "/activeUsers", method = RequestMethod.GET, produces = "application/json") + public List getActiveUsers(HttpServletRequest request) { + List activeUsers = null; + List onlineUsers = new ArrayList<>(); + try { + EPUser user = EPUserUtils.getUserSession(request); + String userId = user.getOrgUserId(); + + activeUsers = searchService.getRelatedUsers(userId); + HashSet usersSet = CollaborateList.getInstance().getAllUserName(); + for (String users : activeUsers) { + if (usersSet.contains(users)) { + onlineUsers.add(users); + } + } + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getActiveUsers failed", e); + } + return onlineUsers; + } + + /** + * Gets only those users that are 'related' to the currently logged-in user. + * + * @param request + * @return Rest response wrapped around a List of String + */ + @RequestMapping(value = "/relatedUsers", method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> activeUsers(HttpServletRequest request) { + EPUser user = EPUserUtils.getUserSession(request); + try { + if (user == null) { + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, "User object is null? - check logs", + new ArrayList<>()); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "activeUsers: searching for user {}", user.getLoginId()); + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", + searchService.getRelatedUsers(user.getLoginId())); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "activeUsers failed", e); + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.getMessage() + " - check logs.", + new ArrayList<>()); + } + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ExternalAppsRestfulController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ExternalAppsRestfulController.java new file mode 100644 index 00000000..41da3357 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ExternalAppsRestfulController.java @@ -0,0 +1,206 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.EPRestrictedRESTfulBaseController; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPRole; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.AdminRolesService; +import org.openecomp.portalapp.portal.service.EPLoginService; +import org.openecomp.portalapp.portal.service.EPRoleService; +import org.openecomp.portalapp.portal.service.FunctionalMenuService; +import org.openecomp.portalapp.portal.service.UserNotificationService; +import org.openecomp.portalapp.portal.transport.EpNotificationItem; +import org.openecomp.portalapp.portal.transport.FavoritesFunctionalMenuItemJson; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.FunctionalMenuItem; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.portal.utils.PortalConstants; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.crossapi.PortalAPIResponse; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestBody; +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.RestController; + +import io.swagger.annotations.ApiOperation; + +@RestController +@RequestMapping(PortalConstants.REST_AUX_API) +@Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class ExternalAppsRestfulController extends EPRestrictedRESTfulBaseController { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ExternalAppsRestfulController.class); + + @Autowired + private FunctionalMenuService functionalMenuService; + + @Autowired + private EPLoginService epLoginService; + + @Autowired + private AdminRolesService adminRolesService; + + @Autowired + private UserNotificationService userNotificationService; + + @Autowired + private EPRoleService epRoleService; + + @ApiOperation(value = "Creates a Portal user notification for roles identified in the content from an external application.", response = PortalAPIResponse.class) + @RequestMapping(value = { "/publishNotification" }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public PortalAPIResponse publishNotification(HttpServletRequest request, + @RequestBody EpNotificationItem notificationItem) throws Exception { + String appKey = request.getHeader("uebkey"); + EPApp app = findEpApp(appKey); + List postRoleIds = new ArrayList(); + for (Long roleId : notificationItem.getRoleIds()) { + EPRole role = epRoleService.getRole(app.getId(), roleId); + if (role != null) + postRoleIds.add(role.getId()); + } + + // --- recreate the user notification object with the POrtal Role Ids + EpNotificationItem postItem = new EpNotificationItem(); + postItem.setRoleIds(postRoleIds); + postItem.setIsForAllRoles("N"); + postItem.setIsForOnlineUsers("N"); + postItem.setActiveYn("Y"); + postItem.setPriority(notificationItem.getPriority()); + postItem.setMsgHeader(notificationItem.getMsgHeader()); + postItem.setMsgDescription(notificationItem.getMsgDescription()); + postItem.setStartTime(notificationItem.getStartTime()); + postItem.setEndTime(notificationItem.getEndTime()); + postItem.setCreatedDate(Calendar.getInstance().getTime()); + // default creator to 1 for now + postItem.setCreatorId(PortalConstants.DEFAULT_NOTIFICATION_CREATOR); + // ---- + + try { + userNotificationService.saveNotification(postItem); + } catch (Exception e) { + return new PortalAPIResponse(false, e.getMessage()); + } + + PortalAPIResponse response = new PortalAPIResponse(true, "success"); + return response; + } + + private EPApp findEpApp(String uebKey) { + List list = null; + StringBuffer criteria = new StringBuffer(); + criteria.append(" where ueb_key = '" + uebKey + "'"); + list = getDataAccessService().getList(EPApp.class, criteria.toString(), null, null); + return (list == null || list.size() == 0) ? null : (EPApp) list.get(0); + } + + @ApiOperation(value = "Gets favorite items within the functional menu for the current user.", response = FavoritesFunctionalMenuItemJson.class, responseContainer="List") + @RequestMapping(value = { "/getFavorites" }, method = RequestMethod.GET, produces = "application/json") + public List getFavoritesForUser(HttpServletRequest request, + HttpServletResponse response) throws Exception { + String loginId = ""; + String userAgent = ""; + List favorites = null; + + loginId = request.getHeader(EPCommonSystemProperties.MDC_LOGIN_ID); + userAgent = MDC.get(EPCommonSystemProperties.PARTNER_NAME); + + EPUser epUser = epLoginService.findUserWithoutPwd(loginId); + logger.info(EELFLoggerDelegate.errorLogger, + "getFavorites request was received from " + userAgent + " for the user " + loginId + "."); + if (epUser == null || epUser.getId() == null) { + logger.error(EELFLoggerDelegate.errorLogger, + "No User record found for the LoginId '" + loginId + "' in the database."); + throw new Exception("Received null for Login-Id."); + } else { + favorites = functionalMenuService.getFavoriteItems(epUser.getId()); + FieldsValidator fieldsValidator = new FieldsValidator(); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + + EcompPortalUtils.logAndSerializeObject(logger, "/getFavorites", "result = ", favorites); + } + + return favorites; + } + + @ApiOperation(value = "Gets functional menu items appropriate for the current user.", response = FunctionalMenuItem.class, responseContainer="List") + @RequestMapping(value = { + "/functionalMenuItemsForUser" }, method = RequestMethod.GET, produces = "application/json") + public List getFunctionalMenuItemsForUser(HttpServletRequest request, + HttpServletResponse response) throws Exception { + String loginId = ""; + String userAgent = ""; + List fnMenuItems = null; + + loginId = request.getHeader("LoginId"); + userAgent = MDC.get(EPCommonSystemProperties.PARTNER_NAME); + + EPUser epUser = epLoginService.findUserWithoutPwd(loginId); + logger.info(EELFLoggerDelegate.errorLogger, "getFunctionalMenuItemsForUser request was received from " + + userAgent + " for the user " + loginId + "."); + if (epUser == null || epUser.getId() == null) { + logger.error(EELFLoggerDelegate.errorLogger, + "No User record found for the LoginId '" + loginId + "' in the database."); + throw new Exception("Received null for Login-Id."); + } else if (adminRolesService.isSuperAdmin(epUser)) { + logger.debug(EELFLoggerDelegate.debugLogger, + "FunctionalMenuHandler: SuperUser, about to call getFunctionalMenuItems()"); + fnMenuItems = functionalMenuService.getFunctionalMenuItems(); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, + "getMenuItemsForAuthUser: about to call getFunctionalMenuItemsForUser()"); + fnMenuItems = functionalMenuService.getFunctionalMenuItemsForUser(epUser.getOrgUserId()); + } + + FieldsValidator fieldsValidator = new FieldsValidator(); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + + EcompPortalUtils.logAndSerializeObject(logger, "/functionalMenuItemsForUser", "result = ", fnMenuItems); + + return fnMenuItems; + } + + @ExceptionHandler(Exception.class) + protected void handleBadRequests(Exception e, HttpServletResponse response) throws IOException { + logger.warn(EELFLoggerDelegate.errorLogger, "Handling bad request", e); + response.sendError(HttpStatus.BAD_REQUEST.value(), e.getMessage()); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/FunctionalMenuController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/FunctionalMenuController.java new file mode 100644 index 00000000..de71766d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/FunctionalMenuController.java @@ -0,0 +1,555 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.cxf.transport.http.HTTPException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.UserProfileService; +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.SharedContext; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.AdminRolesService; +import org.openecomp.portalapp.portal.service.EPAuditService; +import org.openecomp.portalapp.portal.service.FunctionalMenuService; +import org.openecomp.portalapp.portal.service.SearchService; +import org.openecomp.portalapp.portal.service.SharedContextService; +import org.openecomp.portalapp.portal.transport.BusinessCardApplicationRole; +import org.openecomp.portalapp.portal.transport.BusinessCardApplicationRolesList; +import org.openecomp.portalapp.portal.transport.FavoritesFunctionalMenuItem; +import org.openecomp.portalapp.portal.transport.FavoritesFunctionalMenuItemJson; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.FunctionalMenuItem; +import org.openecomp.portalapp.portal.transport.FunctionalMenuItemWithRoles; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.util.EPUserUtils; + +/** + * Supports menus at the top of the Portal app landing page. + */ +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class FunctionalMenuController extends EPRestrictedBaseController { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(FunctionalMenuController.class); + + @Autowired + AdminRolesService adminRolesService; + @Autowired + FunctionalMenuService functionalMenuService; + @Autowired + SharedContextService sharedContextService; + @Autowired + UserProfileService service; + @Autowired + SearchService searchService; + @Autowired + EPAuditService epAuditService; + + /** + * RESTful service method to fetch all the FunctionalMenuItems. + * + * @return List of FunctionalMenuItem objects + */ + @RequestMapping(value = { "/portalApi/functionalMenu" }, method = RequestMethod.GET, produces = "application/json") + public List getMenuItems(HttpServletRequest request, HttpServletResponse response) { + // TODO: should only the superuser be allowed to use this API? + List menuItems = null; + try { + menuItems = functionalMenuService.getFunctionalMenuItems(); + functionalMenuService.assignHelpURLs(menuItems); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenu", "result =", menuItems); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while calling functionalMenu. Details: " + EcompPortalUtils.getStackTrace(e)); + } + return menuItems; + } + + /** + * RESTful service method to fetch all the FunctionalMenuItems, both active + * and inactive, for the EditFunctionalMenu feature. Can only be accessed by + * the portal admin. + * + * @return List of FunctionalMenuItem objects + */ + @RequestMapping(value = { + "/portalApi/functionalMenuForEditing" }, method = RequestMethod.GET, produces = "application/json") + public List getMenuItemsForEditing(HttpServletRequest request, HttpServletResponse response) { + // TODO: should only the superuser be allowed to use this API? + EPUser user = EPUserUtils.getUserSession(request); + List menuItems = null; + try { + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getFunctionalMenuItemDetails"); + } else { + menuItems = functionalMenuService.getFunctionalMenuItems(true); + } + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuForEditing", "result =", menuItems); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while calling functionalMenuForEditing. Details: " + + EcompPortalUtils.getStackTrace(e)); + } + return menuItems; + } + + /** + * RESTful service method to fetch all the FunctionalMenuItems, active + *, for the Functional menu in notification Tree feature. + * + * @return List of FunctionalMenuItem objects + */ + @RequestMapping(value = { + "/portalApi/functionalMenuForNotificationTree" }, method = RequestMethod.GET, produces = "application/json") + public List getMenuItemsForNotifications(HttpServletRequest request, HttpServletResponse response) { + // TODO: should only the superuser be allowed to use this API? + EPUser user = EPUserUtils.getUserSession(request); + List menuItems = null; + try { + menuItems = functionalMenuService.getFunctionalMenuItemsForNotificationTree(true); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuForNotificationTree", "result =", menuItems); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while calling functionalMenuForNotifications. Details: " + + EcompPortalUtils.getStackTrace(e)); + } + return menuItems; + } + + /** + * RESTful service method to fetch all FunctionalMenuItems associated with + * an application. + * + * @return List of FunctionalMenuItem objects + */ + @RequestMapping(value = { + "/portalApi/functionalMenuForApp/{appId}" }, method = RequestMethod.GET, produces = "application/json") + public List getMenuItemsForApp(HttpServletRequest request, @PathVariable("appId") Integer appId) + throws HTTPException { + // TODO: should only the superuser be allowed to use this API? + List menuItems = null; + try { + menuItems = functionalMenuService.getFunctionalMenuItemsForApp(appId); + functionalMenuService.assignHelpURLs(menuItems); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuForApp/" + appId, "result =", menuItems); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while calling functionalMenuForApp. Details: " + + EcompPortalUtils.getStackTrace(e)); + } + return menuItems; + } + + /** + * RESTful service method to fetch all FunctionalMenuItems associated with + * the applications and roles that a user has access to. + * + * @return List of FunctionalMenuItem objects + */ + @RequestMapping(value = { + "/portalApi/functionalMenuForUser/{orgUserId}" }, method = RequestMethod.GET, produces = "application/json") + public List getMenuItemsForUser(HttpServletRequest request, + @PathVariable("orgUserId") String orgUserId) throws HTTPException { + // TODO: should only the superuser be allowed to use this API? + List menuItems = null; + try { + menuItems = functionalMenuService.getFunctionalMenuItemsForUser(orgUserId); + functionalMenuService.assignHelpURLs(menuItems); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuForUser/" + orgUserId, "result =", menuItems); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while calling functionalMenuForUser. Details: " + + EcompPortalUtils.getStackTrace(e)); + } + + return menuItems; + } + + /** + * RESTful service method to fetch all FunctionalMenuItems associated with + * the applications and roles that the authenticated user has access to. + * + * @return List of FunctionalMenuItem objects + */ + @RequestMapping(value = { + "/portalApi/functionalMenuForAuthUser" }, method = RequestMethod.GET, produces = "application/json") + public List getMenuItemsForAuthUser(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List menuItems = null; + try { + if (user == null) { + EcompPortalUtils.setBadPermissions(user, response, "getMenuItemsForAuthUser"); + } else if (adminRolesService.isSuperAdmin(user)) { + menuItems = functionalMenuService.getFunctionalMenuItems(); + } else { + // calculate the menu items + String orgUserId = user.getOrgUserId(); + menuItems = functionalMenuService.getFunctionalMenuItemsForUser(orgUserId); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuForUser/" + orgUserId, "result =", + menuItems); + } + functionalMenuService.assignHelpURLs(menuItems); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while calling getMenuItemsForAuthUser. Details: " + + EcompPortalUtils.getStackTrace(e)); + } + return menuItems; + } + + /** + * RESTful service method to fetch the details for a functional menu item. + * Requirement: you must be the Ecomp portal super admin user. + * + * @return FunctionalMenuItem object + */ + @RequestMapping(value = { + "/portalApi/functionalMenuItemDetails/{menuId}" }, method = RequestMethod.GET, produces = "application/json") + public FunctionalMenuItem getFunctionalMenuItemDetails(HttpServletRequest request, + @PathVariable("menuId") Integer menuId, HttpServletResponse response) throws HTTPException { + // TODO: return FunctionalMenuItemJson + // TODO: modify FunctionalMenuItem to not include the transient fields + FunctionalMenuItem menuItem = null; + try { + EPUser user = EPUserUtils.getUserSession(request); + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getFunctionalMenuItemDetails"); + } else { + menuItem = functionalMenuService.getFunctionalMenuItemDetails(menuId); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuItemDetails/" + menuId, "result =", + menuItem); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while calling functionalMenuItemDetails. Details: " + + EcompPortalUtils.getStackTrace(e)); + } + + return menuItem; + } + + /** + * RESTful service method to create a new menu item. + * + * Requirement: you must be the Ecomp portal super admin user. + */ + @RequestMapping(value = { "/portalApi/functionalMenuItem" }, method = RequestMethod.POST) + public FieldsValidator createFunctionalMenuItem(HttpServletRequest request, + @RequestBody FunctionalMenuItemWithRoles menuItemJson, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + FieldsValidator fieldsValidator = null; + if (!adminRolesService.isSuperAdmin(user)) { + logger.debug(EELFLoggerDelegate.debugLogger, + "FunctionalMenuController.createFunctionalMenuItem bad permissions"); + EcompPortalUtils.setBadPermissions(user, response, "createFunctionalMenuItem"); + } else { + fieldsValidator = functionalMenuService.createFunctionalMenuItem(menuItemJson); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuItem", "POST result =", + response.getStatus()); + } + + return fieldsValidator; + } + + /** + * RESTful service method to update an existing menu item + * + * Requirement: you must be the Ecomp portal super admin user. + * + * @return FieldsValidator + */ + @RequestMapping(value = { "/portalApi/functionalMenuItem" }, method = RequestMethod.PUT) + public FieldsValidator editFunctionalMenuItem(HttpServletRequest request, + @RequestBody FunctionalMenuItemWithRoles menuItemJson, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + FieldsValidator fieldsValidator = null; + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "editFunctionalMenuItem"); + } else { + fieldsValidator = functionalMenuService.editFunctionalMenuItem(menuItemJson); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuItem", "PUT result =", + response.getStatus()); + } + + return fieldsValidator; + } + + /** + * RESTful service method to delete a menu item + * + * @return FieldsValidator + */ + @RequestMapping(value = { "/portalApi/functionalMenuItem/{menuId}" }, method = RequestMethod.DELETE) + public FieldsValidator deleteFunctionalMenuItem(HttpServletRequest request, @PathVariable("menuId") Long menuId, + HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + FieldsValidator fieldsValidator = null; + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "deleteFunctionalMenuItem"); + } else { + fieldsValidator = functionalMenuService.deleteFunctionalMenuItem(menuId); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuItem", "DELETE result =", + response.getStatus()); + } + + return fieldsValidator; + } + + /** + * RESTful service to regenerate table + * + * @param request + * @param response + * + * @return FieldsValidator + */ + @RequestMapping(value = { "/portalApi/regenerateFunctionalMenuAncestors" }, method = RequestMethod.GET) + public FieldsValidator regenerateAncestorTable(HttpServletRequest request, HttpServletResponse response) { + // TODO: should only the superuser be allowed to use this API? + EPUser user = EPUserUtils.getUserSession(request); + FieldsValidator fieldsValidator = null; + + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "deleteFunctionalMenuItem"); + } else { + fieldsValidator = functionalMenuService.regenerateAncestorTable(); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/regenerateAncestorTable", "GET result =", + response.getStatus()); + } + + return fieldsValidator; + } + + /** + * RESful service to set a favorite item. + * + * @return FieldsValidator + */ + @RequestMapping(value = { "/portalApi/setFavoriteItem" }, method = RequestMethod.POST) + public FieldsValidator addFavoriteItem(HttpServletRequest request, + @RequestBody FavoritesFunctionalMenuItem menuItemJson, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + FieldsValidator fieldsValidator = null; + menuItemJson.userId = user.getId(); + fieldsValidator = functionalMenuService.setFavoriteItem(menuItemJson); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/setFavoriteItem", "Post result =", response.getStatus()); + + return fieldsValidator; + } + + /** + * RESTful service to get favorites for the current user as identified in + * the session + * + * @return List of FavoritesFunctionalMenuItemJson + */ + @RequestMapping(value = { + "/portalApi/getFavoriteItems" }, method = RequestMethod.GET, produces = "application/json") + public List getFavoritesForUser(HttpServletRequest request, + HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List favorites = functionalMenuService.getFavoriteItems(user.getId()); + FieldsValidator fieldsValidator = new FieldsValidator(); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/getFavoriteItems", "GET result =", response.getStatus()); + return favorites; + } + + /** + * RESTful service to delete a favorite menu item for the current user as + * identified in the session. + * + * @return FieldsValidator + */ + @RequestMapping(value = { "/portalApi/removeFavoriteItem/{menuId}" }, method = RequestMethod.DELETE) + public FieldsValidator deleteFavoriteItem(HttpServletRequest request, @PathVariable("menuId") Long menuId, + HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + FieldsValidator fieldsValidator = null; + Long userId = user.getId(); + fieldsValidator = functionalMenuService.removeFavoriteItem(userId, menuId); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + EcompPortalUtils.logAndSerializeObject(logger, "/deleteFavoriteItem", "DELETE result =", response.getStatus()); + + return fieldsValidator; + } + + /** + * RESTful service to get user information: user's first and last names, ATT + * UID, email and last-login. (Actually has nothing to do with the real + * functional menu.) First attempts to get the information from the Tomcat + * session (i.e., the CSP cookie); if that fails, calls the shared context + * service to read the information from the database. Gives back what it + * found, any of which may be null, as a JSON collection. + * + * @return JSON collection of key-value pairs shown below. + */ + @RequestMapping(value = { + "/portalApi/functionalMenuStaticInfo" }, method = RequestMethod.GET, produces = "application/json") + public String getFunctionalMenuStaticInfo(HttpServletRequest request, HttpServletResponse response) { + + // Get user details from session + logger.debug(EELFLoggerDelegate.debugLogger, "getFunctionalMenuStaticInfo: getting user info"); + String fnMenuStaticResponse = null; + try { + String orgUserIdStr = null, firstNameStr = null, lastNameStr = null, emailStr = null, lastLogin = null; + EPUser user = EPUserUtils.getUserSession(request); + firstNameStr = user.getFirstName(); + lastNameStr = user.getLastName(); + orgUserIdStr = user.getOrgUserId(); + emailStr = user.getEmail(); + if (emailStr == null || emailStr.equals("")) { + EPUser userResult = searchService.searchUserByUserId(orgUserIdStr); + emailStr = userResult.getEmail(); + } + SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy hh:mm:ssZ"); + Date lastLoginDate = user.getLastLoginDate(); + if (lastLoginDate == null) { + // should never happen + logger.error(EELFLoggerDelegate.errorLogger, "getFunctionalMenuStaticInfo: no last login in session"); + lastLogin = "no last login available"; + } + else { + lastLogin = sdf.format(lastLoginDate); + } + + // If any item is missing from session, try the Shared Context + // service. + SharedContext orgUserIdSC = null, firstNameSC = null, lastNameSC = null, emailSC = null; + String sessionId = request.getSession().getId(); + if (firstNameStr == null) + firstNameSC = sharedContextService.getSharedContext(sessionId, EPCommonSystemProperties.USER_FIRST_NAME); + if (lastNameStr == null) + lastNameSC = sharedContextService.getSharedContext(sessionId, EPCommonSystemProperties.USER_LAST_NAME); + if (emailStr == null) + emailSC = sharedContextService.getSharedContext(sessionId, EPCommonSystemProperties.USER_EMAIL); + if (orgUserIdStr == null) + orgUserIdSC = sharedContextService.getSharedContext(sessionId, EPCommonSystemProperties.USER_ORG_USERID); + + // Build the response + Map map = new HashMap(); + map.put("firstName", + firstNameStr != null ? firstNameStr : (firstNameSC != null ? firstNameSC.getCvalue() : null)); + map.put("lastName", + lastNameStr != null ? lastNameStr : (lastNameSC != null ? lastNameSC.getCvalue() : null)); + map.put("email", emailStr != null ? emailStr : (emailSC != null ? emailSC.getCvalue() : null)); + map.put("userId", orgUserIdStr != null ? orgUserIdStr : (orgUserIdSC != null ? orgUserIdSC.getCvalue() : null)); + map.put("last_login", lastLogin); + JSONObject j = new JSONObject(map); + fnMenuStaticResponse = j.toString(); + // Be chatty in the log + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/functionalMenuStaticInfo", "GET result =", + fnMenuStaticResponse); + } catch (Exception e) { + // Should never happen. + logger.error(EELFLoggerDelegate.errorLogger, "getFunctionalMenuStaticInfo failed", e); + // Return a real error? + // fnMenuStaticResponse = "{ \"status\": \"error\", \"message\": \"" + // + e.toString() + "\" }"; + // But the angular controller expects null on error. + } + return fnMenuStaticResponse; + } + + private Comparator getUserAppRolesComparator = new Comparator() { + public int compare(BusinessCardApplicationRole o1, BusinessCardApplicationRole o2) { + return o1.getAppName().compareTo(o2.getAppName()); + } + }; + + @RequestMapping(value = { + "/portalApi/userApplicationRoles" }, method = RequestMethod.GET, produces = "application/json") + public List getAppList(HttpServletRequest request,@RequestParam("userId") String userId) throws IOException { + + + List AppRoles = null; + try { + List userAppRoleList = functionalMenuService.getUserAppRolesList(userId); + + Collections.sort(userAppRoleList, getUserAppRolesComparator); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userApplicationRoles", "result =", userAppRoleList); + + AppRoles = new ArrayList(); + for(BusinessCardApplicationRole userAppRole: userAppRoleList) + { + boolean found = false; + List roles = null; + + for(BusinessCardApplicationRolesList app :AppRoles) + { + if(app.getAppName().equals(userAppRole.getAppName())) + { + roles= app.getRoleNames(); + roles.add(userAppRole.getRoleName()); + app.setRoleNames(roles); + found = true; + break; + } + } + + if(!found) + { + roles = new ArrayList(); + roles.add(userAppRole.getRoleName()); + AppRoles.add(new BusinessCardApplicationRolesList(userAppRole.getAppName(), roles)); + } + + Collections.sort(roles); + } + } catch (Exception e) { + // TODO Auto-generated catch block + logger.error(EELFLoggerDelegate.errorLogger, "getAppList failed", e); + } + + return AppRoles; + + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/GetAccessController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/GetAccessController.java new file mode 100644 index 00000000..7856dc43 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/GetAccessController.java @@ -0,0 +1,73 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.controller.EPUnRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.GetAccessResult; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.GetAccessService; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.util.EPUserUtils; + +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class GetAccessController extends EPUnRestrictedBaseController { + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(GetAccessController.class); + + @Autowired + GetAccessService getAccessService; + + /** + * Sorts the list by ECOMP function name. + */ + private Comparator getAccessComparator = new Comparator() { + public int compare(GetAccessResult o1, GetAccessResult o2) { + return o1.getAppName().compareTo(o2.getAppName()); + } + }; + + @RequestMapping(value = { "/portalApi/getAppList" }, method = RequestMethod.GET, produces = "application/json") + public List getAppList(HttpServletRequest request) throws IOException { + List appsList = null; + EPUser user = EPUserUtils.getUserSession(request); + appsList = getAccessService.getAppAccessList(user); + Collections.sort(appsList, getAccessComparator); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/getAppList", "result =", appsList); + return appsList; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/HealthCheckController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/HealthCheckController.java new file mode 100644 index 00000000..84731af4 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/HealthCheckController.java @@ -0,0 +1,235 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.MDC; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.controller.EPUnRestrictedBaseController; +import org.openecomp.portalapp.portal.listener.HealthMonitor; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import com.google.gson.Gson; + +/* + * This controller checks the health status and returns JSON response. + * It does not require authentication or session. + */ +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class HealthCheckController extends EPUnRestrictedBaseController { + + private class HealthStatus { + public int statusCode; + @SuppressWarnings("unused") + public String body; + + public HealthStatus(int code, String body) { + this.statusCode = code; + this.body = body; + } + } + + private class HealthStatusInfo { + HealthStatusInfo(String healthCheckComponent) { + this.healthCheckComponent = healthCheckComponent; + this.healthCheckStatus = statusUp; // Default value + this.version = ""; + this.description = statusOk; // Default value + this.hostName = ""; + this.ipAddress = ""; + this.dbClusterStatus = ""; + this.dbPermissions = ""; + } + + @SuppressWarnings("unused") + public String healthCheckComponent; + @SuppressWarnings("unused") + public String healthCheckStatus; + @SuppressWarnings("unused") + public String version; + @SuppressWarnings("unused") + public String description; + @SuppressWarnings("unused") + public String hostName; + @SuppressWarnings("unused") + public String ipAddress; + @SuppressWarnings("unused") + public String dbClusterStatus; + @SuppressWarnings("unused") + public String dbPermissions; + } + + private final String statusUp = "UP"; + private final String statusDown = "DOWN"; + private final String statusOk = "OK"; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(HealthCheckController.class); + + @RequestMapping(value = { "/portalApi/healthCheck" }, method = RequestMethod.GET, produces = "application/json") + public HealthStatus healthCheck(HttpServletRequest request, HttpServletResponse response) { + HealthStatus healthStatus = new HealthStatus(500, ""); + + // Return the status as 500 if it suspended due to manual fail over + if (HealthMonitor.isSuspended) { + healthStatus.body = "Suspended"; + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + MDC.put(EPCommonSystemProperties.RESPONSE_CODE, Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); + return healthStatus; + } + + try { + boolean overallStatus = true; + + List statusCollection = new ArrayList(); + + HealthStatusInfo beInfo = new HealthStatusInfo("BE"); + beInfo.hostName = EcompPortalUtils.getMyHostName(); + beInfo.ipAddress = EcompPortalUtils.getMyIpAdddress(); + if (!HealthMonitor.isBackEndUp()) { + overallStatus = false; + beInfo.healthCheckStatus = statusDown; + beInfo.description = "Check the logs for more details"; + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeHealthCheckError); + } + statusCollection.add(beInfo); + + HealthStatusInfo feInfo = new HealthStatusInfo("FE"); + if (!HealthMonitor.isFrontEndUp()) { + overallStatus = false; + feInfo.healthCheckStatus = statusDown; + feInfo.description = "Check the logs for more details"; + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.FeHealthCheckError); + } + statusCollection.add(feInfo); + + HealthStatusInfo dbInfo = new HealthStatusInfo("DB"); + if (!HealthMonitor.isDatabaseUp()) { + overallStatus = false; + dbInfo.healthCheckStatus = statusDown; + dbInfo.description = "Check the logs for more details"; + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError); + } + + if (!HealthMonitor.isClusterStatusOk()) { + dbInfo.dbClusterStatus = "Problem, check the logs for more details"; + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError); + } + else { + dbInfo.dbClusterStatus = statusOk; + } + + if (!HealthMonitor.isDatabasePermissionsOk()) { + dbInfo.dbPermissions = "Problem, check the logs for more details"; + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError); + } + else { + dbInfo.dbPermissions = statusOk; + } + statusCollection.add(dbInfo); + + HealthStatusInfo uebInfo = new HealthStatusInfo("UEB"); + if (!HealthMonitor.isUebUp()) { + //As per test case review meeting, UEB is considered as critical as DB. Hence commenting + //overallStatus = false; + uebInfo.healthCheckStatus = statusDown; + uebInfo.description = "Check the logs for more details"; + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUebConnectionError); + } + statusCollection.add(uebInfo); + + String json = ""; + try { + json = new Gson().toJson(statusCollection); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput); + } + logger.info(EELFLoggerDelegate.debugLogger, json); + + if (overallStatus) { + healthStatus = new HealthStatus(200, json); + } else { + healthStatus = new HealthStatus(500, json); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + MDC.put(EPCommonSystemProperties.RESPONSE_CODE, Integer.toString(healthStatus.statusCode)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while performing the healthcheck. Details: " + + EcompPortalUtils.getStackTrace(e)); + } + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/healthCheck", "GET result =", response.getStatus()); + + return healthStatus; + } + + @RequestMapping(value = { + "/portalApi/healthCheckSuspend" }, method = RequestMethod.GET, produces = "application/json") + public HealthStatus healthCheckSuspend(HttpServletRequest request, HttpServletResponse response) { + HealthStatus healthStatus = new HealthStatus(500, "Suspended for manual failover mechanism"); + + HealthMonitor.isSuspended = true; + healthStatus.statusCode = 200; + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/healthCheckSuspend", "GET result =", + response.getStatus()); + + return healthStatus; + } + + @RequestMapping(value = { + "/portalApi/healthCheckResume" }, method = RequestMethod.GET, produces = "application/json") + public HealthStatus healthCheckResume(HttpServletRequest request, HttpServletResponse response) { + HealthStatus healthStatus = new HealthStatus(500, "Resumed from manual failover mechanism"); + + HealthMonitor.isSuspended = false; + healthStatus.statusCode = 200; + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/healthCheckResume", "GET result =", + response.getStatus()); + return healthStatus; + } + + + + @RequestMapping(value = { + "/portalApi/ping" }, method = RequestMethod.GET, produces = "application/json") + public HealthStatus ping(HttpServletRequest request, HttpServletResponse response) { + HealthStatus healthStatus = new HealthStatus(200, "OK"); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/ping", "GET result =", + response.getStatus()); + + return healthStatus; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ManifestController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ManifestController.java new file mode 100644 index 00000000..7b4d51f5 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/ManifestController.java @@ -0,0 +1,90 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.HashMap; +import java.util.Map; +import java.util.jar.Attributes; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +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.RestController; + +import org.openecomp.portalsdk.core.controller.RestrictedBaseController; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.ManifestService; + +/** + * This controller responds to a request for the web application manifest, + * returning a JSON with the information that was created at build time. + * + * Manifest entries have names with hyphens, which means Javascript code can't + * simply use the shorthand object.key; instead use object['key']. + */ +@RestController +@Configuration("manifestPortalController") +@EnableAspectJAutoProxy +@RequestMapping("/") +@EPAuditLog +public class ManifestController extends RestrictedBaseController { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ManifestController.class); + + @Autowired + ManifestService manifestService; + + /** + * Gets the webapp manifest contents as a JSON object. + * + * @param request + * @return A map of key-value pairs. On success: + * + *
+	 * { "manifest" : { "key1": "value1", "key2": "value2" } }
+	 *         
+ * + * On failure: + * + *
+	 * { "error": "message" }
+	 *         
+ */ + @RequestMapping(value = { "/portalApi/manifest" }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public Map getManifest(HttpServletRequest request) { + Map response = new HashMap(); + try { + Attributes attributes = manifestService.getWebappManifest(); + response.put("manifest", attributes); + } catch (Exception ex) { + logger.error(EELFLoggerDelegate.errorLogger, "getManifest: failed to read manifest", ex); + response.put("error", "failed to get manifest: " + ex.toString()); + } + return response; + } + +} \ No newline at end of file diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/MicroserviceController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/MicroserviceController.java new file mode 100644 index 00000000..82d70ecd --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/MicroserviceController.java @@ -0,0 +1,141 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.MicroserviceData; +import org.openecomp.portalapp.portal.domain.WidgetCatalog; +import org.openecomp.portalapp.portal.domain.WidgetServiceHeaders; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.ConsulHealthService; +import org.openecomp.portalapp.portal.service.MicroserviceService; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +@SuppressWarnings("unchecked") +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class MicroserviceController extends EPRestrictedBaseController { + + private static final String HTTPS = "https://"; + + String whatService = "widgets-service"; + RestTemplate template = new RestTemplate(); + + @Autowired + private ConsulHealthService consulHealthService; + + @Autowired + private MicroserviceService microserviceService; + + @RequestMapping(value = { "/portalApi/microservices" }, method = RequestMethod.POST) + public PortalRestResponse createMicroservice(HttpServletRequest request, HttpServletResponse response, + @RequestBody MicroserviceData newServiceData) throws Exception { + if (newServiceData == null) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", + "MicroserviceData cannot be null or empty"); + } + long serviceId = microserviceService.saveMicroservice(newServiceData); + + try { + microserviceService.saveServiceParameters(serviceId, newServiceData.getParameterList()); + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", e.getMessage()); + } + + return new PortalRestResponse(PortalRestStatusEnum.OK, "SUCCESS", ""); + } + + @RequestMapping(value = { "/portalApi/microservices" }, method = RequestMethod.GET) + public List getMicroservice(HttpServletRequest request, HttpServletResponse response) + throws Exception { + List list = microserviceService.getMicroserviceData(); + return list; + } + + @RequestMapping(value = { "/portalApi/microservices/{serviceId}" }, method = RequestMethod.PUT) + public PortalRestResponse updateMicroservice(HttpServletRequest request, HttpServletResponse response, + @PathVariable("serviceId") long serviceId, @RequestBody MicroserviceData newServiceData) throws Exception { + + if (newServiceData == null) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", + "MicroserviceData cannot be null or empty"); + } + try { + microserviceService.updateMicroservice(serviceId, newServiceData); + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.OK, "SUCCESS", ""); + } + + @RequestMapping(value = { "/portalApi/microservices/{serviceId}" }, method = RequestMethod.DELETE) + public PortalRestResponse deleteMicroservice(HttpServletRequest request, HttpServletResponse response, + @PathVariable("serviceId") long serviceId) throws Exception { + try { + ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { + }; + // If this service is assoicated with widgets, cannnot be deleted + ResponseEntity> ans = (ResponseEntity>) template.exchange( + HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/widgetCatalog/service/" + serviceId, + HttpMethod.GET, new HttpEntity(WidgetServiceHeaders.getInstance()), typeRef); + List widgets = ans.getBody(); + if(widgets.size() == 0) + microserviceService.deleteMicroservice(serviceId); + else{ + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < widgets.size(); i++){ + sb.append("'" + widgets.get(i).getName() + "' "); + if(i < (widgets.size()-1)){ + sb.append(","); + } + } + return new PortalRestResponse(PortalRestStatusEnum.WARN, "SOME WIDGETS ASSOICATE WITH THIS SERVICE", sb.toString()); + } + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.OK, "SUCCESS", ""); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/MicroserviceProxyController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/MicroserviceProxyController.java new file mode 100644 index 00000000..6724d4c6 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/MicroserviceProxyController.java @@ -0,0 +1,108 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.EPUnRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.WidgetParameterResult; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.MicroserviceProxyService; +import org.openecomp.portalapp.util.EPUserUtils; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpClientErrorException; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +@SuppressWarnings("unchecked") +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class MicroserviceProxyController extends EPUnRestrictedBaseController { + + @Autowired + private MicroserviceProxyService microserviceProxyService; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MicroserviceProxyController.class); + + @RequestMapping(value = { "/portalApi/microservice/proxy/{serviceId}" }, method = { + RequestMethod.GET }, produces = "application/json") + public String getMicroserviceProxy(HttpServletRequest request, HttpServletResponse response, + @PathVariable("serviceId") long serviceId) throws Exception { + EPUser user = EPUserUtils.getUserSession(request); + String answer = ""; + try{ + answer = microserviceProxyService.proxyToDestination(serviceId, user, request); + }catch(HttpClientErrorException e){ + //Check whether the error message is valid JSON format + boolean valid = true; + ObjectMapper objectMapper = new ObjectMapper(); + try{ + objectMapper.readTree(e.getResponseBodyAsString()); + } catch(JsonProcessingException exception){ + valid = false; + } + if(valid) + return e.getResponseBodyAsString(); + else + return "{\"error\":\""+ e.getResponseBodyAsString() +"\"}"; + } + return answer; + } + + @RequestMapping(value = { "/portalApi/microservice/proxy/parameter/{widgetId}" }, method = { + RequestMethod.GET }, produces = "application/json") + public String getMicroserviceProxyByWidgetId(HttpServletRequest request, HttpServletResponse response, + @PathVariable("widgetId") long widgetId) throws Exception { + EPUser user = EPUserUtils.getUserSession(request); + String answer = ""; + try{ + answer = microserviceProxyService.proxyToDestinationByWidgetId(widgetId, user, request); + }catch(HttpClientErrorException e){ + //Check whether the error message is valid JSON format + boolean valid = true; + ObjectMapper objectMapper = new ObjectMapper(); + try{ + objectMapper.readTree(e.getResponseBodyAsString()); + } catch(JsonProcessingException exception){ + valid = false; + } + if(valid) + return e.getResponseBodyAsString(); + else + return "{\"error\":\""+ e.getResponseBodyAsString() +"\"}"; + } + return answer; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/PortalAdminController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/PortalAdminController.java new file mode 100644 index 00000000..2499ec66 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/PortalAdminController.java @@ -0,0 +1,200 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import org.openecomp.portalsdk.core.domain.AuditLog; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.AuditService; +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPRole; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EcompAuditLog; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.logging.aop.EPEELFLoggerAdvice; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.service.AdminRolesService; +import org.openecomp.portalapp.portal.service.PortalAdminService; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.PortalAdmin; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.util.EPUserUtils; + +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class PortalAdminController extends EPRestrictedBaseController { + @Autowired + PortalAdminService portalAdminService; + @Autowired + AdminRolesService adminRolesService; + @Autowired + AuditService auditService; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(PortalAdminController.class); + + @RequestMapping(value = { "/portalApi/portalAdmins" }, method = RequestMethod.GET, produces = "application/json") + public List getPortalAdmins(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List portalAdmins = null; + if (user == null) { + logger.debug(EELFLoggerDelegate.debugLogger, "PortalAdminController.getPortalAdmins, null user"); + EcompPortalUtils.setBadPermissions(user, response, "getPortalAdmins"); + } else if (!adminRolesService.isSuperAdmin(user)) { + logger.debug(EELFLoggerDelegate.debugLogger, "PortalAdminController.getPortalAdmins, bad permissions"); + EcompPortalUtils.setBadPermissions(user, response, "createPortalAdmin"); + } else { + // return the list of portal admins + portalAdmins = portalAdminService.getPortalAdmins(); + logger.debug(EELFLoggerDelegate.debugLogger, "portalAdmins: called getPortalAdmins()"); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/getPortalAdmins", "result =", portalAdmins); + } + + return portalAdmins; + } + + /** + * RESTful service method to create a new portal admin. Requirement: you + * must be the Ecomp portal super admin user. + */ + + @RequestMapping(value = { "/portalApi/portalAdmin" }, method = RequestMethod.POST) + public FieldsValidator createPortalAdmin(HttpServletRequest request, @RequestBody String sbcid, + HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + FieldsValidator fieldsValidator = null; + if (user == null) { + logger.debug(EELFLoggerDelegate.debugLogger, "PortalAdminController.createPortalAdmin, null user"); + EcompPortalUtils.setBadPermissions(user, response, "createPortalAdmin"); + } else if (!adminRolesService.isSuperAdmin(user)) { + logger.debug(EELFLoggerDelegate.debugLogger, "PortalAdminController.createPortalAdmin bad permissions"); + EcompPortalUtils.setBadPermissions(user, response, "createPortalAdmin"); + } else { + fieldsValidator = portalAdminService.createPortalAdmin(sbcid); + int statusCode = fieldsValidator.httpStatusCode.intValue(); + response.setStatus(statusCode); + if (statusCode == 200) { + AuditLog auditLog = new AuditLog(); + auditLog.setUserId(user.getId()); + auditLog.setActivityCode(EcompAuditLog.CD_ACTIVITY_ADD_PORTAL_ADMIN); + auditLog.setAffectedRecordId(sbcid); + auditService.logActivity(auditLog, null); + + MDC.put(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP, EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + MDC.put(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP, EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + logger.info(EELFLoggerDelegate.auditLogger, + EPLogUtil.formatAuditLogMessage("PortalAdminController.createPortalAdmin", + EcompAuditLog.CD_ACTIVITY_ADD_PORTAL_ADMIN, user.getOrgUserId(), sbcid, + "A new Portal Admin has been added")); + MDC.remove(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP); + } + } + EcompPortalUtils.logAndSerializeObject(logger, "/portalAdmin", "POST result =", response.getStatus()); + + return fieldsValidator; + } + + @RequestMapping(value = { "/portalApi/portalAdmin/{userInfo}" }, method = RequestMethod.DELETE) + public FieldsValidator deletePortalAdmin(HttpServletRequest request, @PathVariable("userInfo") String userInfo, + HttpServletResponse response) { + int userIdIdx = userInfo.indexOf("-"); + Long userId = null; + String sbcid = null; + FieldsValidator fieldsValidator = null; + try{ + if(userIdIdx==-1){ + logger.error(EELFLoggerDelegate.errorLogger,"deletePortalAdmin missing userId"); + return fieldsValidator; + }else{ + String userIdStr = userInfo.substring(0, userIdIdx); + userId = Long.valueOf(userIdStr); + sbcid = userInfo.substring(userIdIdx+1, userInfo.length()); + } + }catch(Exception e){ + logger.error(EELFLoggerDelegate.errorLogger,"deletePortalAdmin error while parsing the userInfo",e); + } + EPUser user = EPUserUtils.getUserSession(request); + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "deletePortalAdmin"); + } else { + fieldsValidator = portalAdminService.deletePortalAdmin(userId); + int statusCode = fieldsValidator.httpStatusCode.intValue(); + response.setStatus(statusCode); + if (statusCode == 200) { + AuditLog auditLog = new AuditLog(); + auditLog.setUserId(user.getId()); + auditLog.setActivityCode(EcompAuditLog.CD_ACTIVITY_DELETE_PORTAL_ADMIN); + auditLog.setAffectedRecordId(sbcid); + auditService.logActivity(auditLog, null); + + MDC.put(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP, EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + MDC.put(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP, EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + logger.info(EELFLoggerDelegate.auditLogger, + EPLogUtil.formatAuditLogMessage("PortalAdminController.deletePortalAdmin", + EcompAuditLog.CD_ACTIVITY_DELETE_PORTAL_ADMIN, user.getOrgUserId(), sbcid, + "A Portal Admin has been deleted")); + MDC.remove(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP); + } + } + EcompPortalUtils.logAndSerializeObject(logger, "/portalAdmin", "DELETE result =", response.getStatus()); + + return fieldsValidator; + } + + @RequestMapping(value = { + "/portalApi/adminAppsRoles/{appId}" }, method = RequestMethod.GET, produces = "application/json") + public List getRolesByApp(HttpServletRequest request, @PathVariable("appId") Long appId, + HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List rolesByApp = null; + + try { + if (user == null) { + EcompPortalUtils.setBadPermissions(user, response, "getUserApps"); + } else { + rolesByApp = adminRolesService.getRolesByApp(user, appId); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while performing getRolesByApp operation, Details: " + + EcompPortalUtils.getStackTrace(e)); + } + + return rolesByApp; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/RoleManageController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/RoleManageController.java new file mode 100644 index 00000000..d567269f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/RoleManageController.java @@ -0,0 +1,166 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.controller.core.RoleController; +import org.openecomp.portalapp.controller.core.RoleFunctionListController; +import org.openecomp.portalapp.controller.core.RoleListController; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.ModelAndView; + +/** + * Proxies REST calls to role-management functions that arrive on paths + * /portalApi/* over to controller methods provided by the SDK-Core library. + * Those controller methods are mounted on paths not exposed by the Portal FE. + */ +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class RoleManageController extends EPRestrictedBaseController { + + @Autowired + private RoleController roleController; + + @Autowired + private RoleListController roleListController; + + @Autowired + private RoleFunctionListController roleFunctionListController; + + /** + * Calls an SDK-Core library method that gets the available roles and writes + * them to the request object. Portal specifies a Hibernate mappings from + * the Role class to the fn_role_v view, which ensures that only Portal + * (app_id is null) roles are fetched. + * + * Any method declared void (no return value) or returning null causes the + * audit log aspect method to declare failure. TODO: should return a JSON + * string. + * + * @param request + * @param response + */ + @RequestMapping(value = { "/portalApi/get_roles" }, method = RequestMethod.GET) + public void getRoles(HttpServletRequest request, HttpServletResponse response) { + getRoleListController().getRoles(request, response); + } + + @RequestMapping(value = { "/portalApi/role_list/toggleRole" }, method = RequestMethod.POST) + public PortalRestResponse toggleRole(HttpServletRequest request, HttpServletResponse response) { + PortalRestResponse portalRestResponse = null; + try{ + getRoleListController().toggleRole(request, response); + portalRestResponse = new PortalRestResponse(PortalRestStatusEnum.OK, "success", null); + }catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "failure", e.getMessage()); + } + return portalRestResponse; + } + + @RequestMapping(value = { "/portalApi/role_list/removeRole" }, method = RequestMethod.POST) + public ModelAndView removeRole(HttpServletRequest request, HttpServletResponse response) throws Exception { + return getRoleListController().removeRole(request, response); + } + + @RequestMapping(value = { "/portalApi/role/saveRole" }, method = RequestMethod.POST) + public ModelAndView saveRole(HttpServletRequest request, HttpServletResponse response) throws Exception { + return getRoleController().saveRole(request, response); + } + + @RequestMapping(value = { "/portalApi/role/removeRoleFunction" }, method = RequestMethod.POST) + public ModelAndView removeRoleRoleFunction(HttpServletRequest request, HttpServletResponse response) + throws Exception { + return getRoleController().removeRoleFunction(request, response); + } + + @RequestMapping(value = { "/portalApi/role/addRoleFunction" }, method = RequestMethod.POST) + public ModelAndView addRoleRoRoleFunction(HttpServletRequest request, HttpServletResponse response) + throws Exception { + return getRoleController().addRoleFunction(request, response); + } + + @RequestMapping(value = { "/portalApi/role/removeChildRole" }, method = RequestMethod.POST) + public ModelAndView removeChildRole(HttpServletRequest request, HttpServletResponse response) throws Exception { + return getRoleController().removeChildRole(request, response); + } + + @RequestMapping(value = { "/portalApi/role/addChildRole" }, method = RequestMethod.POST) + public ModelAndView addChildRole(HttpServletRequest request, HttpServletResponse response) throws Exception { + return getRoleController().addChildRole(request, response); + } + + @RequestMapping(value = { "/portalApi/get_role" }, method = RequestMethod.GET) + public void getRole(HttpServletRequest request, HttpServletResponse response) { + getRoleController().getRole(request, response); + } + + @RequestMapping(value = { "/portalApi/get_role_functions" }, method = RequestMethod.GET) + public void getRoleFunctionList(HttpServletRequest request, HttpServletResponse response) { + getRoleFunctionListController().getRoleFunctionList(request, response); + } + + @RequestMapping(value = { "/portalApi/role_function_list/saveRoleFunction" }, method = RequestMethod.POST) + public void saveRoleFunction(HttpServletRequest request, HttpServletResponse response, @RequestBody String roleFunc) throws Exception { + getRoleFunctionListController().saveRoleFunction(request, response, roleFunc); + } + + @RequestMapping(value = { "/portalApi/role_function_list/removeRoleFunction" }, method = RequestMethod.POST) + public void removeRoleFunction(HttpServletRequest request, HttpServletResponse response, @RequestBody String roleFunc) throws Exception { + getRoleFunctionListController().removeRoleFunction(request, response, roleFunc); + } + + public RoleListController getRoleListController() { + return roleListController; + } + + public void setRoleListController(RoleListController roleListController) { + this.roleListController = roleListController; + } + + public RoleController getRoleController() { + return roleController; + } + + public void setRoleController(RoleController roleController) { + this.roleController = roleController; + } + + public RoleFunctionListController getRoleFunctionListController() { + return roleFunctionListController; + } + + public void setRoleFunctionListController(RoleFunctionListController roleFunctionListController) { + this.roleFunctionListController = roleFunctionListController; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/SharedContextRestController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/SharedContextRestController.java new file mode 100644 index 00000000..a59a4579 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/SharedContextRestController.java @@ -0,0 +1,343 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.EPRestrictedRESTfulBaseController; +import org.openecomp.portalapp.portal.domain.SharedContext; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.SharedContextService; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.PortalConstants; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.swagger.annotations.ApiOperation; + +/** + * The shared-context feature allows onboarded applications to share data among + * themselves easily for a given session. It basically implements a Java map: + * put or get a key-value pair within a map identified by a session ID. + * + * This REST endpoint listens on the Portal app server and answers requests made + * by back-end application servers. Reads and writes values to the database + * using a Hibernate service to ensure all servers in a high-availability + * cluster see the same data. + */ +@Configuration +@RestController +@RequestMapping(PortalConstants.REST_AUX_API + "/context") +@EnableAspectJAutoProxy +@EPAuditLog +public class SharedContextRestController extends EPRestrictedRESTfulBaseController { + + /** + * Model for a one-element JSON object returned by many methods. + */ + class SharedContextJsonResponse { + String response; + } + + /** + * Access to the database + */ + @Autowired + private SharedContextService contextService; + + /** + * Logger for debug etc. + */ + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SharedContextRestController.class); + + /** + * Reusable JSON (de)serializer + */ + private final ObjectMapper mapper = new ObjectMapper(); + + /** + * Gets a value for the specified context and key (RESTful service method). + * + * @param ctxtId + * ID that identifies the context, usually the ECOMP Portal + * session key. + * @param ckey + * Key for the key-value pair to fetch + * @return JSON with shared context object; response=null if not found. + */ + @ApiOperation(value = "Gets a value for the specified context and key.", response = SharedContext.class) + @RequestMapping(value = { "/get" }, method = RequestMethod.GET, produces = "application/json") + public String getContext(HttpServletRequest request, @RequestParam String context_id, @RequestParam String ckey) + throws Exception { + + logger.debug(EELFLoggerDelegate.debugLogger, "getContext for ID " + context_id + ", key " + ckey); + if (context_id == null || ckey == null) + throw new Exception("Received null for context_id and/or ckey"); + + SharedContext context = contextService.getSharedContext(context_id, ckey); + String jsonResponse = ""; + if (context == null) + jsonResponse = convertResponseToJSON(context); + else + jsonResponse = mapper.writeValueAsString(context); + + return jsonResponse; + } + + /** + * Gets user information for the specified context (RESTful service method). + * + * @param ctxtId + * ID that identifies the context, usually the ECOMP Portal + * session key. + * + * @return List of shared-context objects as JSON; should have user's first + * name, last name and email address; null if none found + */ + @ApiOperation(value = "Gets user information for the specified context.", response = SharedContext.class, responseContainer = "List") + @RequestMapping(value = { "/get_user" }, method = RequestMethod.GET, produces = "application/json") + public String getUserContext(HttpServletRequest request, @RequestParam String context_id) throws Exception { + + logger.debug(EELFLoggerDelegate.debugLogger, "getUserContext for ID " + context_id); + if (context_id == null) + throw new Exception("Received null for context_id"); + + List listSharedContext = new ArrayList(); + SharedContext firstNameContext = contextService.getSharedContext(context_id, + EPCommonSystemProperties.USER_FIRST_NAME); + SharedContext lastNameContext = contextService.getSharedContext(context_id, + EPCommonSystemProperties.USER_LAST_NAME); + SharedContext emailContext = contextService.getSharedContext(context_id, EPCommonSystemProperties.USER_EMAIL); + SharedContext attuidContext = contextService.getSharedContext(context_id, + EPCommonSystemProperties.USER_ORG_USERID); + if (firstNameContext != null) + listSharedContext.add(firstNameContext); + if (lastNameContext != null) + listSharedContext.add(lastNameContext); + if (emailContext != null) + listSharedContext.add(emailContext); + if (attuidContext != null) + listSharedContext.add(attuidContext); + String jsonResponse = convertResponseToJSON(listSharedContext); + return jsonResponse; + } + + /** + * Tests for presence of the specified key in the specified context (RESTful + * service method). + * + * @param context_id + * ID that identifies the context, usually the ECOMP Portal + * session key. + * @param ckey + * Key for the key-value pair to test + * @return JSON with result indicating whether the context and key were + * found. + */ + @ApiOperation(value = "Tests for presence of the specified key in the specified context.", response = SharedContextJsonResponse.class) + @RequestMapping(value = { "/check" }, method = RequestMethod.GET, produces = "application/json") + public String checkContext(HttpServletRequest request, @RequestParam String context_id, @RequestParam String ckey) + throws Exception { + + logger.debug(EELFLoggerDelegate.debugLogger, "checkContext for " + context_id + ", key " + ckey); + if (context_id == null || ckey == null) + throw new Exception("Received null for contextId and/or key"); + + String response = null; + SharedContext context = contextService.getSharedContext(context_id, ckey); + if (context != null) + response = "exists"; + + String jsonResponse = convertResponseToJSON(response); + return jsonResponse; + } + + /** + * Removes the specified key in the specified context (RESTful service + * method). + * + * @param context_id + * ID that identifies the context, usually the ECOMP Portal + * session key. + * @param ckey + * Key for the key-value pair to remove + * @return JSON with result indicating whether the context and key were + * found. + */ + @ApiOperation(value = "Removes the specified key in the specified context.", response = SharedContextJsonResponse.class) + @RequestMapping(value = { "/remove" }, method = RequestMethod.GET, produces = "application/json") + public String removeContext(HttpServletRequest request, @RequestParam String context_id, @RequestParam String ckey) + throws Exception { + + logger.debug(EELFLoggerDelegate.debugLogger, "removeContext for " + context_id + ", key " + ckey); + if (context_id == null || ckey == null) + throw new Exception("Received null for contextId and/or key"); + + SharedContext context = contextService.getSharedContext(context_id, ckey); + String response = null; + if (context != null) { + contextService.deleteSharedContext(context); + response = "removed"; + } + + String jsonResponse = convertResponseToJSON(response); + return jsonResponse; + } + + /** + * Clears all key-value pairs in the specified context (RESTful service + * method). + * + * @param context_id + * ID that identifies the context, usually the ECOMP Portal + * session key. + * @return JSON with result indicating the number of key-value pairs + * removed. + */ + @ApiOperation(value = "Clears all key-value pairs in the specified context.", response = SharedContextJsonResponse.class) + @RequestMapping(value = { "/clear" }, method = RequestMethod.GET, produces = "application/json") + public String clearContext(HttpServletRequest request, @RequestParam String context_id) throws Exception { + + logger.debug(EELFLoggerDelegate.debugLogger, "clearContext for " + context_id); + if (context_id == null) + throw new Exception("clearContext: Received null for contextId"); + + int count = contextService.deleteSharedContexts(context_id); + String jsonResponse = convertResponseToJSON(Integer.toString(count)); + return jsonResponse; + } + + /** + * Sets a context value for the specified context and key (RESTful service + * method). Creates the context if no context with the specified ID-key pair + * exists, overwrites the value if it exists already. + * + * @param userJson + * JSON block with these tag-value pairs: + *
    + *
  • context_id: ID that identifies the context + *
  • ckey: Key for the key-value pair to store + *
  • cvalue: Value to store + *
+ * @return JSON with result indicating whether the value was added (key not + * previously known) or replaced (key previously known). + */ + @ApiOperation(value = "Sets a context value for the specified context and key. Creates the context if no context with the specified ID-key pair exists, overwrites the value if it exists already.", response = SharedContextJsonResponse.class) + @RequestMapping(value = { "/set" }, method = RequestMethod.POST, produces = "application/json") + public String setContext(HttpServletRequest request, @RequestBody String userJson) throws Exception { + + @SuppressWarnings("unchecked") + Map userData = mapper.readValue(userJson, Map.class); + // Use column names as JSON tags + final String contextId = (String) userData.get("context_id"); + final String key = (String) userData.get("ckey"); + final String value = (String) userData.get("cvalue"); + if (contextId == null || key == null) + throw new Exception("setContext: received null for contextId and/or key"); + + logger.debug(EELFLoggerDelegate.debugLogger, "setContext: ID " + contextId + ", key " + key + "->" + value); + String response = null; + SharedContext existing = contextService.getSharedContext(contextId, key); + if (existing == null) { + contextService.addSharedContext(contextId, key, value); + } else { + existing.setCvalue(value); + contextService.saveSharedContext(existing); + } + response = existing == null ? "added" : "replaced"; + String jsonResponse = convertResponseToJSON(response); + return jsonResponse; + } + + /** + * Creates a two-element JSON object tagged "response". + * + * @param responseBody + * @return JSON object as String + * @throws JsonProcessingException + */ + private String convertResponseToJSON(String responseBody) throws JsonProcessingException { + Map responseMap = new HashMap(); + responseMap.put("response", responseBody); + String response = mapper.writeValueAsString(responseMap); + return response; + } + + /** + * Converts a list of SharedContext objects to a JSON array. + * + * @param contextList + * @return JSON array as String + * @throws JsonProcessingException + */ + private String convertResponseToJSON(List contextList) throws JsonProcessingException { + String jsonArray = mapper.writeValueAsString(contextList); + return jsonArray; + } + + /** + * Creates a JSON object with the content of the shared context; null is ok. + * + * @param responseBody + * @return tag "response" with collection of context object's fields + * @throws JsonProcessingException + */ + private String convertResponseToJSON(SharedContext context) throws JsonProcessingException { + Map responseMap = new HashMap(); + responseMap.put("response", context); + String responseBody = mapper.writeValueAsString(responseMap); + return responseBody; + } + + /** + * Handles any exception thrown by a method in this controller. + * + * @param e + * Exception + * @param response + * HttpServletResponse + * @throws IOException + */ + @ExceptionHandler(Exception.class) + protected void handleBadRequests(Exception e, HttpServletResponse response) throws IOException { + logger.error(EELFLoggerDelegate.errorLogger, "handleBadRequest caught exception", e); + response.sendError(HttpStatus.BAD_REQUEST.value(), e.getMessage()); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/TicketEventController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/TicketEventController.java new file mode 100644 index 00000000..0dd344d7 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/TicketEventController.java @@ -0,0 +1,179 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.UserNotificationService; +import org.openecomp.portalapp.portal.transport.EpNotificationItem; +import org.openecomp.portalapp.portal.transport.EpRoleNotificationItem; +import org.openecomp.portalapp.portal.utils.PortalConstants; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.swagger.annotations.ApiOperation; + +/** + * Receives messages from the Collaboration Bus (C-BUS) notification and event + * brokering tool. Creates notifications for ECOMP Portal users. + */ +@RestController +@RequestMapping(PortalConstants.REST_AUX_API) +@Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class TicketEventController implements BasicAuthenticationController { + + @Autowired + private UserNotificationService userNotificationService; + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(TicketEventController.class); + + public boolean isAuxRESTfulCall() { + return true; + } + + private final ObjectMapper mapper = new ObjectMapper(); + + @ApiOperation(value = "Accepts messages from external ticketing systems and creates notifications for Portal users.", response = PortalRestResponse.class) + @RequestMapping(value = { "/ticketevent" }, method = RequestMethod.POST) + public PortalRestResponse handleRequest(HttpServletRequest request, HttpServletResponse response, + @RequestBody String ticketEventJson) throws Exception { + + logger.debug(EELFLoggerDelegate.debugLogger, "Ticket Event notification" + ticketEventJson); + PortalRestResponse portalResponse = new PortalRestResponse<>(); + try { + JsonNode ticketEventNotif = mapper.readTree(ticketEventJson); + + // Reject request if required fields are missing. + String error = validateTicketEventMessage(ticketEventNotif); + if (error != null) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + portalResponse.setMessage(error); + response.setStatus(400); + return portalResponse; + } + + EpNotificationItem epItem = new EpNotificationItem(); + epItem.setCreatedDate(new Date()); + epItem.setIsForOnlineUsers("Y"); + epItem.setIsForAllRoles("N"); + epItem.setActiveYn("Y"); + //JsonNode application = ticketEventNotif.get("application"); + JsonNode event = ticketEventNotif.get("event"); + JsonNode header = event.get("header"); + JsonNode body = event.get("body"); + epItem.setMsgDescription(body.toString()); + Long eventDate = body.get("eventDate").asLong(); + + String assetID = body.get("assetID").asText(); + epItem.setMsgHeader(assetID); + String eventSource = header.get("eventSource").asText(); + epItem.setMsgSource(eventSource); + epItem.setStartTime(new Date(eventDate)); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(epItem.getStartTime()); + int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); + calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth + 30); + epItem.setEndTime(calendar.getTime()); + String severityString= (body.get("severity").toString()).substring(1, 2); + Long severity=Long.parseLong(severityString); + epItem.setPriority(severity); + epItem.setCreatorId(null); + Set roles = new HashSet<>(); + JsonNode SubscriberInfo = ticketEventNotif.get("SubscriberInfo"); + JsonNode userList = SubscriberInfo.get("UserList"); + String UserIds[] = userList.toString().replace("[", "").replace("]", "").trim().replace("\"", "") + .split(","); + List users = userNotificationService.getUsersByOrgIds(Arrays.asList(UserIds)); + for (String userId : UserIds) { + EpRoleNotificationItem roleNotifItem = new EpRoleNotificationItem(); + for (EPUser user : users) { + if (user.getOrgUserId().equals(userId)) { + roleNotifItem.setRecvUserId(user.getId().intValue()); + roles.add(roleNotifItem); + break; + } + } + + } + epItem.setRoles(roles); + userNotificationService.saveNotification(epItem); + + portalResponse.setStatus(PortalRestStatusEnum.OK); + portalResponse.setMessage("processEventNotification: notification created"); + portalResponse.setResponse("NotificationId is :" + epItem.notificationId); + } catch (Exception ex) { + portalResponse.setStatus(PortalRestStatusEnum.ERROR); + response.setStatus(400); + portalResponse.setMessage(ex.toString()); + } + return portalResponse; + } + + /** + * Validates that mandatory fields are present. + * + * @param ticketEventNotif + * @return Error message if a problem is found; null if all is well. + */ + private String validateTicketEventMessage(JsonNode ticketEventNotif) { + JsonNode application = ticketEventNotif.get("application"); + JsonNode event = ticketEventNotif.get("event"); + JsonNode header = event.get("header"); + JsonNode body = event.get("body"); + JsonNode SubscriberInfo = ticketEventNotif.get("SubscriberInfo"); + if (application == null) + return "application is mandatory"; + if (body == null) + return "body is mandatory"; + if (body.get("assetID") == null) + return "Asset Id is mandatory"; + if (body.get("eventDate") == null) + return "Event Date is mandatory"; + if (header.get("eventSource") == null) + return "Message Source is mandatory"; + if (SubscriberInfo.get("UserList") == null) + return "At least one user Id is mandatory"; + return null; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/UserNotificationController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/UserNotificationController.java new file mode 100644 index 00000000..777fb91d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/UserNotificationController.java @@ -0,0 +1,216 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalsdk.core.web.support.UserUtils; +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EcompAppRole; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.FunctionalMenuService; +import org.openecomp.portalapp.portal.service.UserNotificationService; +import org.openecomp.portalapp.portal.transport.EpNotificationItem; +import org.openecomp.portalapp.portal.transport.EpNotificationItemVO; +import org.openecomp.portalapp.portal.transport.EpRoleNotificationItem; +import org.openecomp.portalapp.portal.transport.FunctionalMenuRole; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.util.EPUserUtils; + +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class UserNotificationController extends EPRestrictedBaseController { + + @Autowired + FunctionalMenuService functionalMenuService; + + @Autowired + UserNotificationService userNotificationService; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(UserNotificationController.class); + + @RequestMapping(value = { + "/portalApi/getFunctionalMenuRole" }, method = RequestMethod.GET, produces = "application/json") + public List getMenuIdRoleId(HttpServletRequest request, HttpServletResponse response) { + // EPUser user = EPUserUtils.getUserSession(request); + List menuRoleList = null; + menuRoleList = functionalMenuService.getFunctionalMenuRole(); + return menuRoleList; + } + + @RequestMapping(value = { + "/portalApi/getNotifications" }, method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getNotifications(HttpServletRequest request, + HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + PortalRestResponse> portalRestResponse = null; + try { + List notificationList = userNotificationService.getNotifications(user.getId()); + portalRestResponse = new PortalRestResponse>(PortalRestStatusEnum.OK, "success", + notificationList); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAllAppsAndContacts failed", e); + portalRestResponse = new PortalRestResponse>(PortalRestStatusEnum.ERROR, + e.getMessage(), null); + } + return portalRestResponse; + } + + @RequestMapping(value = { + "/portalApi/getAdminNotifications" }, method = RequestMethod.GET, produces = "application/json") + public List getAdminNotifications(HttpServletRequest request, HttpServletResponse response) { + List adminNotificationList = null; + adminNotificationList = userNotificationService.getAdminNotificationVOS(); + return adminNotificationList; + } + + @RequestMapping(value = "/portalApi/saveNotification", method = RequestMethod.POST, produces = "application/json") + public PortalRestResponse save(HttpServletRequest request, HttpServletResponse response, + @RequestBody EpNotificationItem notificationItem) { + + if (notificationItem == null || notificationItem.getMsgHeader() == null) + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", + "Notification Header cannot be null or empty"); + if (notificationItem.getEndTime().compareTo(notificationItem.getStartTime()) < 0) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", + "End Time should be greater than start time"); + } + + if ((notificationItem.getIsForAllRoles() == "N") && notificationItem.getRoleIds().isEmpty()) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", + "No Roles Ids Exist for the selected Roles"); + } + + Long creatorId = UserUtils.getUserIdAsLong(request); + notificationItem.setCreatorId(creatorId); + + // Front-end date picker does not accept a time value, so all + // values are the start of the chosen day in the local time zone. + // Move the end time value to the very end of the chosen day. + // Avoid Calendar.getDefault() which uses the server's locale. + Long endTime = notificationItem.getEndTime().getTime(); + endTime += (23 * 3600 + 59 * 60 + 59) * 1000; + notificationItem.getEndTime().setTime(endTime); + + try { + userNotificationService.saveNotification(notificationItem); + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.OK, "SUCCESS", ""); + } + + @RequestMapping(value = { + "/portalApi/notificationUpdateRate" }, method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> getNotificationUpdateRate(HttpServletRequest request) { + try { + String updateRate = SystemProperties.getProperty(EPCommonSystemProperties.NOTIFICATION_UPDATE_RATE); + String updateDuration = SystemProperties.getProperty(EPCommonSystemProperties.NOTIFICATION_UPDATE_DURATION); + Integer rateInMiliSec = Integer.valueOf(updateRate) * 1000; + Integer durationInMiliSec = Integer.valueOf(updateDuration) * 1000; + Map results = new HashMap(); + results.put("updateRate", String.valueOf(rateInMiliSec)); + results.put("updateDuration", String.valueOf(durationInMiliSec)); + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", results); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getNotificationUpdateRate failed", e); + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.toString(), null); + } + } + + @RequestMapping(value = { + "/portalApi/notificationRead" }, method = RequestMethod.GET, produces = "application/json") + public PortalRestResponse> notificationRead( + @RequestParam("notificationId") String notificationID, HttpServletRequest request) { + try { + userNotificationService.setNotificationRead(Long.parseLong(notificationID), UserUtils.getUserId(request)); + return new PortalRestResponse<>(PortalRestStatusEnum.OK, "success", null); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "notificationRead failed", e); + return new PortalRestResponse<>(PortalRestStatusEnum.ERROR, e.toString(), null); + } + } + + @RequestMapping(value = { + "/portalApi/getNotificationHistory" }, method = RequestMethod.GET, produces = "application/json") + public List getNotificationHistory(HttpServletRequest request, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List notificationList = null; + notificationList = userNotificationService.getNotificationHistoryVO(user.getId()); + return notificationList; + } + + @RequestMapping(value = { "/portalApi/notificationRole/{notificationId}/roles" }, method = { + RequestMethod.GET }, produces = "application/json") + public List testGetRoles(HttpServletRequest request, @PathVariable("notificationId") Long notificationId) { + List NotifRoles = userNotificationService.getNotificationRoles(notificationId); + ArrayList rolesList = new ArrayList(); + for (EpRoleNotificationItem notifRole : NotifRoles) { + rolesList.add(notifRole.roleId); + } + return rolesList; + } + + @RequestMapping(value = { "/portalApi/getNotificationAppRoles" }, method = { + RequestMethod.GET }, produces = "application/json") + public List getNotificationAppRoles(HttpServletRequest request, HttpServletResponse response) { + List epAppRoleList = null; + try { + epAppRoleList = userNotificationService.getAppRoleList(); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while performing UserNofiticationController.getNotificationAppRoles. Details: ", + e); + } + return epAppRoleList; + } + + @RequestMapping(value = { + "/portalApi/getMessageRecipients" }, method = RequestMethod.GET, produces = "application/json") + public List getMessageRecipients(@RequestParam("notificationId") Long notificationID) { + // EPUser user = EPUserUtils.getUserSession(request); + List messageUserRecipients = null; + messageUserRecipients = userNotificationService.getMessageRecipients(notificationID); + return messageUserRecipients; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/UserRolesController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/UserRolesController.java new file mode 100644 index 00000000..719b7d45 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/UserRolesController.java @@ -0,0 +1,435 @@ +/*- + * ================================================================================ + * ECOMP Portal SDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.cxf.transport.http.HTTPException; +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPRole; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EPUserAppCatalogRoles; +import org.openecomp.portalapp.portal.domain.EcompAuditLog; +import org.openecomp.portalapp.portal.domain.ExternalSystemAccess; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.logging.aop.EPEELFLoggerAdvice; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.service.AdminRolesService; +import org.openecomp.portalapp.portal.service.ApplicationsRestClientService; +import org.openecomp.portalapp.portal.service.SearchService; +import org.openecomp.portalapp.portal.service.UserRolesService; +import org.openecomp.portalapp.portal.transport.AppNameIdIsAdmin; +import org.openecomp.portalapp.portal.transport.AppWithRolesForUser; +import org.openecomp.portalapp.portal.transport.AppsListWithAdminRole; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.RoleInAppForUser; +import org.openecomp.portalapp.portal.transport.UserApplicationRoles; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.util.EPUserUtils; +import org.openecomp.portalsdk.core.domain.AuditLog; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.restful.domain.EcompRole; +import org.openecomp.portalsdk.core.service.AuditService; +import org.openecomp.portalsdk.core.service.RoleService; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class UserRolesController extends EPRestrictedBaseController { + + @Autowired + SearchService searchService; + @Autowired + AdminRolesService adminRolesService; + @Autowired + UserRolesService userRolesService; + @Autowired + ApplicationsRestClientService applicationsRestClientService; + @Autowired + RoleService roleService; + @Autowired + AuditService auditService; + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(UserRolesController.class); + + static final String FAILURE = "failure"; + + /** + * RESTful service method to fetch users in the WebPhone external service + * + * @return array of found users as json + */ + @RequestMapping(value = { "/portalApi/queryUsers" }, method = RequestMethod.GET, produces = "application/json") + public String getPhoneBookSearchResult(HttpServletRequest request, @RequestParam("search") String searchString, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + String searchResult = null; + if (!adminRolesService.isSuperAdmin(user) && !adminRolesService.isAccountAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getPhoneBookSearchResult"); + } else { + searchString = searchString.trim(); + if (searchString.length() > 2) { + searchResult = searchService.searchUsersInPhoneBook(searchString); + } else { + logger.info(EELFLoggerDelegate.errorLogger, "getPhoneBookSearchResult - too short search string: " + searchString); + } + } + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/queryUsers", "result =", searchResult); + + return searchResult; + } + + /** + * RESTful service method to fetch applications where user is admin + * + * @return for GET: array of all applications with boolean isAdmin=true/false for each application + */ + @RequestMapping(value = { "/portalApi/adminAppsRoles" }, method = { RequestMethod.GET }, produces = "application/json") + public AppsListWithAdminRole getAppsWithAdminRoleStateForUser(HttpServletRequest request, @RequestParam("user") String orgUserId, + HttpServletResponse response) { + + EPUser user = EPUserUtils.getUserSession(request); + AppsListWithAdminRole result = null; + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getAppsWithAdminRoleStateForUser"); + } else { + if (EcompPortalUtils.legitimateUserId(orgUserId)) { + result = adminRolesService.getAppsWithAdminRoleStateForUser(orgUserId); + } else { + logger.info(EELFLoggerDelegate.errorLogger, "getAppsWithAdminRoleStateForUser - parms error, no Organization User ID"); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + } + } + + StringBuilder adminAppRoles = new StringBuilder(); + if(result != null && result.appsRoles.size() >= 1) { + adminAppRoles.append("User '" + result.orgUserId + "' has admin role to the apps = {"); + for(AppNameIdIsAdmin adminAppRole : result.appsRoles) { + if (adminAppRole.isAdmin) { + adminAppRoles.append(adminAppRole.appName + ", "); + } + } + adminAppRoles.append("}."); + } else { + adminAppRoles.append("User '" + result.orgUserId + "' has no Apps with Admin Role."); + } + logger.info(EELFLoggerDelegate.errorLogger, adminAppRoles.toString()); + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/adminAppsRoles", "get result =", result); + + return result; + } + + @RequestMapping(value = { "/portalApi/adminAppsRoles" }, method = { RequestMethod.PUT }, produces = "application/json") + public FieldsValidator putAppsWithAdminRoleStateForUser(HttpServletRequest request, @RequestBody AppsListWithAdminRole newAppsListWithAdminRoles, + HttpServletResponse response) { + + //newAppsListWithAdminRoles.appsRoles + FieldsValidator fieldsValidator = new FieldsValidator(); + StringBuilder newAppRoles = new StringBuilder(); + if(newAppsListWithAdminRoles != null && newAppsListWithAdminRoles.appsRoles.size() >= 1) { + newAppRoles.append("User '" + newAppsListWithAdminRoles.orgUserId + "' has admin role to the apps = { "); + for(AppNameIdIsAdmin adminAppRole : newAppsListWithAdminRoles.appsRoles) { + if (adminAppRole.isAdmin) { + newAppRoles.append(adminAppRole.appName + " ,"); + } + } + newAppRoles.deleteCharAt(newAppRoles.length()-1); + newAppRoles.append("}."); + } else { + newAppRoles.append("User '" + newAppsListWithAdminRoles.orgUserId + "' has no Apps with Admin Role."); + } + logger.info(EELFLoggerDelegate.errorLogger, newAppRoles.toString()); + + EPUser user = EPUserUtils.getUserSession(request); + boolean changesApplied = false; + + if (!adminRolesService.isSuperAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "putAppsWithAdminRoleStateForUser"); + } else { + changesApplied = adminRolesService.setAppsWithAdminRoleStateForUser(newAppsListWithAdminRoles); + AuditLog auditLog = new AuditLog(); + auditLog.setUserId(user.getId()); + auditLog.setActivityCode(EcompAuditLog.CD_ACTIVITY_UPDATE_ACCOUNT_ADMIN); + auditLog.setAffectedRecordId(newAppsListWithAdminRoles.orgUserId); + auditLog.setComments(newAppRoles.toString()); + auditService.logActivity(auditLog, null); + + MDC.put(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP,EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + MDC.put(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP,EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + logger.info(EELFLoggerDelegate.auditLogger, EPLogUtil.formatAuditLogMessage("UserRolesController.putAppsWithAdminRoleStateForUser", + EcompAuditLog.CD_ACTIVITY_UPDATE_ACCOUNT_ADMIN, user.getOrgUserId(), newAppsListWithAdminRoles.orgUserId, newAppRoles.toString())); + MDC.remove(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP); + } + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/adminAppsRoles", "put result =", changesApplied); + + return fieldsValidator; + } + + @RequestMapping(value = { "/portalApi/userAppRoles" }, method = { RequestMethod.GET }, produces = "application/json") + public List getAppRolesForUser(HttpServletRequest request, @RequestParam("user") String orgUserId, @RequestParam("app") Long appid, HttpServletResponse response) { + EPUser user = EPUserUtils.getUserSession(request); + List result = null; + String feErrorString = ""; + if (!adminRolesService.isAccountAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "getAppRolesForUser"); + feErrorString = EcompPortalUtils.getFEErrorString(true, response.getStatus()); + } else { + if (EcompPortalUtils.legitimateUserId(orgUserId)) { + result = userRolesService.getAppRolesForUser(appid, orgUserId); + int responseCode = EcompPortalUtils.getExternalAppResponseCode(); + if (responseCode != 0 && responseCode != 200) { + // external error + response.setStatus(responseCode); + feErrorString = EcompPortalUtils.getFEErrorString(false, responseCode); + } else if (result == null) { + // If the result is null, there was an internal ecomp error in the service call. + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + feErrorString = EcompPortalUtils.getFEErrorString(true, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } else { + logger.info(EELFLoggerDelegate.errorLogger, "getAppRolesForUser - no Organization User ID"); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + feErrorString = EcompPortalUtils.getFEErrorString(true, HttpServletResponse.SC_BAD_REQUEST); + } + } + + StringBuilder sbUserApps = new StringBuilder(); + if (result!=null && result.size()>=1) { + sbUserApps.append("User '" + orgUserId + "' has Roles={"); + for(RoleInAppForUser appRole : result) { + if (appRole.isApplied) { + sbUserApps.append(appRole.roleName + ", "); + } + } + sbUserApps.append("} assigned to the appId '" + appid + "'."); + } else { + //Not sure creating an empty object will make any difference + //but would like to give it a shot for defect #DE221057 + if (result==null) { + result = new ArrayList(); + } + sbUserApps.append("User '" + orgUserId + "' and appid " + appid + " has no roles"); + } + logger.info(EELFLoggerDelegate.errorLogger, sbUserApps.toString()); + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userAppRoles", "get result =", result); + if (feErrorString != "") { + logger.debug(EELFLoggerDelegate.debugLogger, "LR: FEErrorString to header: "+feErrorString); + + response.addHeader("FEErrorString", feErrorString); + response.addHeader("Access-Control-Expose-Headers", "FEErrorString"); + } + return result; + } + + @RequestMapping(value = { "/portalApi/userAppRoles" }, method = { RequestMethod.PUT }, produces = "application/json") + public FieldsValidator putAppWithUserRoleStateForUser(HttpServletRequest request, @RequestBody AppWithRolesForUser newAppRolesForUser, + HttpServletResponse response) { + FieldsValidator fieldsValidator = new FieldsValidator(); + StringBuilder sbUserApps = new StringBuilder(); + if (newAppRolesForUser!=null) { + sbUserApps.append("User '" + newAppRolesForUser.orgUserId); + if (newAppRolesForUser.appRoles!=null && newAppRolesForUser.appRoles.size()>=1) { + sbUserApps.append("' has roles = { "); + for(RoleInAppForUser appRole : newAppRolesForUser.appRoles) { + if (appRole.isApplied) { + sbUserApps.append(appRole.roleName + " ,"); + } + } + sbUserApps.deleteCharAt(sbUserApps.length()-1); + sbUserApps.append("} assigned for the app " + newAppRolesForUser.appId); + } else { + sbUserApps.append("' has no roles assigned for app " + newAppRolesForUser.appId); + } + } + logger.info(EELFLoggerDelegate.applicationLogger, "putAppWithUserRoleStateForUser: {}", sbUserApps.toString()); + + EPUser user = EPUserUtils.getUserSession(request); + boolean changesApplied = false; + if (!adminRolesService.isAccountAdmin(user)) { + EcompPortalUtils.setBadPermissions(user, response, "putAppWithUserRoleStateForUser"); + } else { + changesApplied = userRolesService.setAppWithUserRoleStateForUser(user, newAppRolesForUser); + if (changesApplied){ + logger.info(EELFLoggerDelegate.applicationLogger, "putAppWithUserRoleStateForUser: succeeded for app {}, user {}", + newAppRolesForUser.appId, newAppRolesForUser.orgUserId); + AuditLog auditLog = new AuditLog(); + auditLog.setUserId(user.getId()); + auditLog.setActivityCode(EcompAuditLog.CD_ACTIVITY_UPDATE_USER); + auditLog.setAffectedRecordId(newAppRolesForUser.orgUserId); + auditLog.setComments(sbUserApps.toString()); + MDC.put(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP, EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + auditService.logActivity(auditLog, null); + MDC.put(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP,EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + logger.info(EELFLoggerDelegate.auditLogger, EPLogUtil.formatAuditLogMessage("UserRolesController.putAppWithUserRoleStateForUser", + EcompAuditLog.CD_ACTIVITY_UPDATE_USER, user.getOrgUserId(), newAppRolesForUser.orgUserId, sbUserApps.toString())); + MDC.remove(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP); + } else { + logger.error(EELFLoggerDelegate.errorLogger, "putAppWithUserRoleStateForUser: failed for app {}, user {}", + newAppRolesForUser.appId, newAppRolesForUser.orgUserId); + } + } + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userAppRoles", "put result =", changesApplied); + return fieldsValidator; + } + + @RequestMapping(value = { "/portalApi/updateRemoteUserProfile" }, method = { RequestMethod.GET }, produces = "application/json") + public PortalRestResponse updateRemoteUserProfile(HttpServletRequest request,HttpServletResponse response) { + + String updateRemoteUserFlag = FAILURE; + try { + //saveNewUser = userService.saveNewUser(newUser); + String orgUserId = request.getParameter("loginId"); + Long appId = Long.parseLong(request.getParameter("appId")); + userRolesService.updateRemoteUserProfile(orgUserId, appId); + + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.OK, updateRemoteUserFlag, e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.ERROR, updateRemoteUserFlag, ""); + + } + + @RequestMapping(value = { "/portalApi/app/{appId}/users" }, method = { RequestMethod.GET }, produces = "application/json") + public List getUsersFromAppEndpoint(HttpServletRequest request, @PathVariable("appId") Long appId) throws HTTPException { + try { + logger.debug(EELFLoggerDelegate.debugLogger, "/portalApi/app/{}/users was invoked", appId); + List appUsers = userRolesService.getUsersFromAppEndpoint(appId); + return appUsers; + } catch(Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while performing UserRolesController.getUsersFromAppEndpoint. Details: " + EcompPortalUtils.getStackTrace(e)); + return new ArrayList(); + } + } + + @RequestMapping(value = { "/portalApi/app/{appId}/roles" }, method = { RequestMethod.GET }, produces = "application/json") + public List testGetRoles(HttpServletRequest request, @PathVariable("appId") Long appId) throws HTTPException { + EcompRole[] appRoles = applicationsRestClientService.get(EcompRole[].class, appId, "/roles"); + List rolesList = Arrays.asList(appRoles); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/app/{appId}/roles", "response for appId=" + appId, rolesList); + + return rolesList; + } + + @RequestMapping(value = { "/portalApi/admin/import/app/{appId}/roles" }, method = { RequestMethod.GET }, produces = "application/json") + public List importRolesFromRemoteApplication(HttpServletRequest request, @PathVariable("appId") Long appId) throws HTTPException { + List rolesList = userRolesService.importRolesFromRemoteApplication(appId); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/admin/import/app/{appId}/roles", "response for appId=" + appId, rolesList); + + return rolesList; + } + + @RequestMapping(value = { "/portalApi/app/{appId}/user/{orgUserId}/roles" }, method = { RequestMethod.GET }, produces = "application/json") + public EcompRole testGetRoles(HttpServletRequest request, @PathVariable("appId") Long appId, @PathVariable("orgUserId") String orgUserId) throws Exception { + if (!EcompPortalUtils.legitimateUserId(orgUserId)) { + String msg = "Error /user//roles not legitimate orgUserId = " + orgUserId; + logger.error(EELFLoggerDelegate.errorLogger, msg); + throw new Exception(msg); + } + EcompRole[] roles = applicationsRestClientService.get(EcompRole[].class, appId, String.format("/user/%s/roles", orgUserId)); + if (roles.length != 1) { + String msg = "Error /user//roles returned array. expected size 1 recieved size = " + roles.length; + logger.error(EELFLoggerDelegate.errorLogger, msg); + throw new Exception(msg); + } + + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/app/{appId}/user/{orgUserId}/roles", "response for appId='" + appId + "' and orgUserId='" + orgUserId + "'", roles[0]); + return roles[0]; + } + + @RequestMapping(value = { "/portalApi/saveUserAppRoles" }, method = { RequestMethod.PUT }, produces = "application/json") + public FieldsValidator putAppWithUserRoleRequest(HttpServletRequest request, @RequestBody AppWithRolesForUser newAppRolesForUser, + HttpServletResponse response) { + FieldsValidator fieldsValidator = null; + try { + + EPUser user = EPUserUtils.getUserSession(request); + fieldsValidator = userRolesService.putUserAppRolesRequest(newAppRolesForUser, user); + response.setStatus(fieldsValidator.httpStatusCode.intValue()); + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "putAppWithUserRoleRequest failed", e); + + } + // return fieldsValidator; + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/saveUserAppRoles", "PUT result =", + response.getStatus()); + return fieldsValidator; + } + + @RequestMapping(value = { "/portalApi/appCatalogRoles" }, method = { + RequestMethod.GET }, produces = "application/json") + public List getUserAppCatalogRoles(HttpServletRequest request, + @RequestParam("appName") String appName) { + EPUser user = EPUserUtils.getUserSession(request); + List userAppRoleList = null; + try { + userAppRoleList = userRolesService.getUserAppCatalogRoles(user, appName); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "putUserWidgetsSortPref failed", e); + + } + Collections.sort(userAppRoleList, getUserAppCatalogRolesComparator); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/userApplicationRoles", "result =", userAppRoleList); + + return userAppRoleList; + + } + private Comparator getUserAppCatalogRolesComparator = new Comparator() { + public int compare(EPUserAppCatalogRoles o1, EPUserAppCatalogRoles o2) { + return o1.getRolename().compareTo(o2.getRolename()); + } + }; + + @RequestMapping(value = "/portalApi/externalRequestAccessSystem", method = RequestMethod.GET, produces = "application/json") + public ExternalSystemAccess readExternalRequestAccess(HttpServletRequest request) { + ExternalSystemAccess result = null; + try { + result = userRolesService.getExternalRequestAccess(); + EcompPortalUtils.logAndSerializeObject(logger, "/portalApi/externalRequestAccessSystem", "GET result =", result); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger,"readExternalRequestAccess failed: " + e.getMessage()); + } + return result; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WebAnalyticsExtAppController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WebAnalyticsExtAppController.java new file mode 100644 index 00000000..f1d77a3f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WebAnalyticsExtAppController.java @@ -0,0 +1,157 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.io.IOUtils; +import org.openecomp.portalapp.controller.EPRestrictedRESTfulBaseController; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EcompAuditLog; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.logging.aop.EPEELFLoggerAdvice; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.service.AppsCacheService; +import org.openecomp.portalapp.portal.transport.Analytics; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.PortalConstants; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.crossapi.PortalAPIResponse; +import org.openecomp.portalsdk.core.service.AuditService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestBody; +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.RestController; + +import io.swagger.annotations.ApiOperation; + +@RestController +@RequestMapping(PortalConstants.REST_AUX_API) +@Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class WebAnalyticsExtAppController extends EPRestrictedRESTfulBaseController{ + + private static final String APP_KEY = "uebkey"; + + @Autowired + AuditService auditService; + + @Autowired + AppsCacheService appCacheService; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(WebAnalyticsExtAppController.class); + + protected boolean isAuxRESTfulCall() { + return true; + } + + /* + * Answers requests from partner applications for a file that is expected to + * contain javascript to support web analytics. + * + * @param request + * @return + * @throws Exception + */ + @ApiOperation(value = "Gets javascript with functions that support gathering and reporting web analytics.", response = String.class) + @RequestMapping(value = { "/analytics" }, method = RequestMethod.GET, produces = "application/javascript") + public String getAnalyticsScript(HttpServletRequest request) throws Exception { + String responseText = ""; + final String fileName = "analytics.txt"; + InputStream analyticsFileStream = null; + try { + analyticsFileStream = this.getClass().getClassLoader().getResourceAsStream(fileName); + responseText = IOUtils.toString(analyticsFileStream, StandardCharsets.UTF_8.name()); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Error reading contents of the file " + fileName, e); + } finally { + if (analyticsFileStream != null) + analyticsFileStream.close(); + } + + String feURLContext = SystemProperties.getProperty("frontend_url"); + String feURL = feURLContext.substring(0, feURLContext.lastIndexOf('/')); + + responseText = responseText.replace("PORTAL_ENV_URL", feURL); + return responseText; + } + + /** + * Accepts data from partner applications with web analytics data. + * + * @param request + * @param analyticsMap + * @param response + * @return + * @throws Exception + */ + @RequestMapping(value = { "/storeAnalytics" }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + @ApiOperation(value = "Accepts data from partner applications with web analytics data.", response = PortalAPIResponse.class) + public PortalAPIResponse storeAnalyticsScript(HttpServletRequest request, @RequestBody Analytics analyticsMap) throws Exception { + try{ + MDC.put(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP,EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + MDC.put(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP,EPEELFLoggerAdvice.getCurrentDateTimeUTC()); + String appName = ""; + try { + + String appKeyValue = request.getHeader(APP_KEY); + if(appKeyValue == null || appKeyValue.equals("")) { + logger.error(EELFLoggerDelegate.errorLogger, " App Key unavailable; Proceeding with null app name"); + } else { + EPApp appRecord = appCacheService.getAppForAnalytics(appKeyValue); + if(appRecord == null){ + logger.error(EELFLoggerDelegate.errorLogger, " App could not be found for the key "+ appKeyValue); + } + else appName = appRecord.getName(); + + } + + } catch(Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, " Error retrieving Application to capture app name for analytics; Proceeding with empty app name"); + } + + + + logger.info(EELFLoggerDelegate.auditLogger, EPLogUtil.formatStoreAnalyticsAuditLogMessage(analyticsMap.getUserId(),appName, "WebAnalyticsExtAppController.postWebAnalyticsData", + EcompAuditLog.CD_ACTIVITY_STORE_ANALYTICS, analyticsMap.getAction(),analyticsMap.getPage(),analyticsMap.getFunction(),analyticsMap.getType())); + MDC.remove(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP); + + PortalAPIResponse response = new PortalAPIResponse(true, "success"); + return response; + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "storeAnalytics failed", e); + PortalAPIResponse response = new PortalAPIResponse(true, "error"); + return response; + } + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WidgetsCatalogController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WidgetsCatalogController.java new file mode 100644 index 00000000..f69a6484 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WidgetsCatalogController.java @@ -0,0 +1,376 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.EPRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.MicroserviceData; +import org.openecomp.portalapp.portal.domain.MicroserviceParameter; +import org.openecomp.portalapp.portal.domain.WidgetCatalog; +import org.openecomp.portalapp.portal.domain.WidgetCatalogParameter; +import org.openecomp.portalapp.portal.domain.WidgetParameterResult; +import org.openecomp.portalapp.portal.domain.WidgetServiceHeaders; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestStatusEnum; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.ConsulHealthService; +import org.openecomp.portalapp.portal.service.MicroserviceService; +import org.openecomp.portalapp.portal.service.WidgetParameterService; +import org.openecomp.portalapp.util.EPUserUtils; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.multipart.commons.CommonsMultipartResolver; + +@SuppressWarnings("unchecked") +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class WidgetsCatalogController extends EPRestrictedBaseController { + + private static final String HTTPS = "https://"; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(WidgetsCatalogController.class); + RestTemplate template = new RestTemplate(); + String whatService = "widgets-service"; + + @Autowired + private ConsulHealthService consulHealthService; + + @Autowired + private MicroserviceService microserviceService; + + @Autowired + private WidgetParameterService widgetParameterService; + + @Bean + public CommonsMultipartResolver multipartResolver() { + return new CommonsMultipartResolver(); + } + + static { + // for localhost testing only + javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() { + + public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) { + if (hostname.equals("localhost")) { + return true; + } + return false; + } + }); + } + + @RequestMapping(value = { "/portalApi/microservices/widgetCatalog/{loginName}" }, method = RequestMethod.GET) + public List getUserWidgetCatalog(HttpServletRequest request, HttpServletResponse response, + @PathVariable("loginName") String loginName) throws RestClientException, Exception { + List widgets = new ArrayList<>(); + try { + ResponseEntity ans = template.exchange( + HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/widgetCatalog/" + loginName, + HttpMethod.GET, new HttpEntity(WidgetServiceHeaders.getInstance()), ArrayList.class); + widgets = ans.getBody(); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getUserWidgetCatalog failed", e); + // returning null because null help check on the UI if there was a + // communication problem with Microservice. + return null; + } + return widgets; + } + + @RequestMapping(value = { "/portalApi/microservices/widgetCatalog" }, method = RequestMethod.GET) + public List getWidgetCatalog(HttpServletRequest request, HttpServletResponse response) + throws RestClientException, Exception { + List widgets = new ArrayList<>(); + try { + ResponseEntity ans = template.exchange( + HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/widgetCatalog", + HttpMethod.GET, new HttpEntity(WidgetServiceHeaders.getInstance()), ArrayList.class); + widgets = ans.getBody(); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getWidgetCatalog failed", e); + // returning null because null help check on the UI if there was a + // communication problem with Microservice. + return null; + } + return widgets; + } + + @RequestMapping(value = { + "/portalApi/microservices/widgetCatalog/{widgetId}" }, method = RequestMethod.PUT, produces = "application/json") + public void updateWidgetCatalog(HttpServletRequest request, HttpServletResponse response, + @RequestBody WidgetCatalog newWidgetCatalog, @PathVariable("widgetId") long widgetId) + throws RestClientException, Exception { + template.exchange( + HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/widgetCatalog/" + widgetId, + HttpMethod.PUT, new HttpEntity(newWidgetCatalog, WidgetServiceHeaders.getInstance()), String.class); + } + + @RequestMapping(value = { "/portalApi/microservices/widgetCatalog/{widgetId}" }, method = RequestMethod.DELETE) + public void deleteOnboardingWidget(HttpServletRequest request, HttpServletResponse response, + @PathVariable("widgetId") long widgetId) throws RestClientException, Exception { + template.exchange( + HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/widgetCatalog/" + widgetId, + HttpMethod.DELETE, new HttpEntity(WidgetServiceHeaders.getInstance()), String.class); + } + + @RequestMapping(value = { "/portalApi/microservices/widgetCatalog/{widgetId}" }, method = RequestMethod.POST) + public String updateWidgetCatalogWithFiles(HttpServletRequest request, HttpServletResponse response, + @PathVariable("widgetId") long widgetId) throws RestClientException, Exception { + MultipartHttpServletRequest mRequest; + MultiValueMap multipartRequest = new LinkedMultiValueMap<>(); + String fileName; + String tmp_folder = "/tmp/"; + String respond = null; + FileOutputStream fo = null; + try { + mRequest = (MultipartHttpServletRequest) request; + MultipartFile mFile = mRequest.getFile("file"); + fileName = mFile.getOriginalFilename(); + fo = new FileOutputStream(tmp_folder + fileName); + fo.write(mFile.getBytes()); + + HttpHeaders header = new HttpHeaders(); + header.setContentType(MediaType.MULTIPART_FORM_DATA); + multipartRequest.add("file", new FileSystemResource(tmp_folder + fileName)); + multipartRequest.add("widget", request.getParameter("newWidget")); + respond = template.postForObject( + HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/widgetCatalog/" + widgetId, + new HttpEntity<>(multipartRequest, WidgetServiceHeaders.getInstance()), String.class); + File f = new File(tmp_folder + fileName); + f.delete(); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "updateWidgetCatalogWithFiles failed", e); + } finally { + try { + if (fo != null) + fo.close(); + } catch (IOException e) { + logger.error(EELFLoggerDelegate.errorLogger, "updateWidgetCatalogWithFiles failed 2", e); + } + } + return respond; + } + + @RequestMapping(value = { "/portalApi/microservices/widgetCatalog" }, method = RequestMethod.POST) + public String createWidgetCatalog(HttpServletRequest request, HttpServletResponse response) + throws RestClientException, Exception { + MultipartHttpServletRequest mRequest; + MultiValueMap multipartRequest = new LinkedMultiValueMap<>(); + String fileName; + String tmp_folder = "/tmp/"; + String respond = null; + FileOutputStream fo = null; + try { + mRequest = (MultipartHttpServletRequest) request; + MultipartFile mFile = mRequest.getFile("file"); + fileName = mFile.getOriginalFilename(); + fo = new FileOutputStream(tmp_folder + fileName); + fo.write(mFile.getBytes()); + + HttpHeaders header = new HttpHeaders(); + header.setContentType(MediaType.MULTIPART_FORM_DATA); + multipartRequest.add("file", new FileSystemResource(tmp_folder + fileName)); + multipartRequest.add("widget", request.getParameter("newWidget")); + + respond = template.postForObject( + HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/widgetCatalog", + new HttpEntity<>(multipartRequest, WidgetServiceHeaders.getInstance()), String.class); + File f = new File(tmp_folder + fileName); + f.delete(); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "createWidgetCatalog failed", e); + } finally { + try { + if (fo != null) + fo.close(); + } catch (IOException e) { + logger.error(EELFLoggerDelegate.errorLogger, "createWidgetCatalog failed 2", e); + } + } + return respond; + } + + @RequestMapping(value = "/portalApi/microservices/{widgetId}/framework.js", method = RequestMethod.GET) + public String getWidgetFramework(HttpServletRequest request, HttpServletResponse response, + @PathVariable("widgetId") long widgetId) throws RestClientException, Exception { + return template.getForObject(HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/" + widgetId + "/framework.js", String.class, + WidgetServiceHeaders.getInstance()); + } + + @RequestMapping(value = "/portalApi/microservices/{widgetId}/controller.js", method = RequestMethod.GET) + public String getWidgetController(HttpServletRequest request, HttpServletResponse response, + @PathVariable("widgetId") long widgetId) throws RestClientException, Exception { + return template.getForObject(HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/" + widgetId + "/controller.js", String.class, + WidgetServiceHeaders.getInstance()); + } + + @RequestMapping(value = "/portalApi/microservices/{widgetId}/style.css", method = RequestMethod.GET) + public String getWidgetCSS(HttpServletRequest request, HttpServletResponse response, + @PathVariable("widgetId") long widgetId) throws RestClientException, Exception { + return template.getForObject(HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/" + widgetId + "/styles.css", String.class, + WidgetServiceHeaders.getInstance()); + } + + @RequestMapping(value = { "/portalApi/microservices/parameters/{widgetId}" }, method = RequestMethod.GET) + public PortalRestResponse> getWidgetParameterResult(HttpServletRequest request, + HttpServletResponse response, @PathVariable("widgetId") long widgetId) throws Exception { + EPUser user = EPUserUtils.getUserSession(request); + + List list = new ArrayList<>(); + Long serviceId = template.exchange( + HTTPS + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/widgetCatalog/parameters/" + widgetId, + HttpMethod.GET, new HttpEntity(WidgetServiceHeaders.getInstance()), Long.class).getBody(); + if (serviceId == null) { + // return ok/sucess and no service parameter for this widget + return new PortalRestResponse>(PortalRestStatusEnum.WARN, + "No service parameters for this widget", list); + } else { + List defaultParam = microserviceService.getParametersById(serviceId); + for (MicroserviceParameter param : defaultParam) { + WidgetParameterResult user_result = new WidgetParameterResult(); + user_result.setParam_id(param.getId()); + user_result.setDefault_value(param.getPara_value()); + user_result.setParam_key(param.getPara_key()); + WidgetCatalogParameter userValue = widgetParameterService.getUserParamById(widgetId, user.getId(), + param.getId()); + if (userValue == null) + user_result.setUser_value(param.getPara_value()); + else { + user_result.setUser_value(userValue.getUser_value()); + } + list.add(user_result); + } + } + return new PortalRestResponse>(PortalRestStatusEnum.OK, "SUCCESS", list); + } + + @RequestMapping(value = { "/portalApi/microservices/services/{paramId}" }, method = RequestMethod.GET) + public List getUserParameterById(HttpServletRequest request, HttpServletResponse response, + @PathVariable("paramId") long paramId) throws Exception { + List list = widgetParameterService.getUserParameterById(paramId); + return list; + } + + @RequestMapping(value = { "/portalApi/microservices/services/{paramId}" }, method = RequestMethod.DELETE) + public void deleteUserParameterById(HttpServletRequest request, HttpServletResponse response, + @PathVariable("paramId") long paramId) throws Exception { + widgetParameterService.deleteUserParameterById(paramId); + } + + @RequestMapping(value = { "/portalApi/microservices/download/{widgetId}" }, method = RequestMethod.GET) + public void doDownload(HttpServletRequest request, HttpServletResponse response, + @PathVariable("widgetId") long widgetId) throws RestClientException, Exception { + + ServletContext context = request.getServletContext(); + byte[] byteFile = template.exchange(HTTPS + consulHealthService.getServiceLocation(whatService) + "/widget/microservices/download/" + widgetId, + HttpMethod.GET, new HttpEntity(WidgetServiceHeaders.getInstance()), byte[].class).getBody(); + String fileLocation = widgetId + ".zip"; + FileOutputStream stream = new FileOutputStream(fileLocation); + stream.write(byteFile); + stream.close(); + + File downloadFile = new File(fileLocation); + FileInputStream inputStream = new FileInputStream(downloadFile); + String mimeType = context.getMimeType(downloadFile.getPath()); + if (mimeType == null) { + mimeType = "application/octet-stream"; + } + + response.setContentType(mimeType); + response.setContentLength((int) downloadFile.length()); + String headerKey = "Content-Disposition"; + String headerValue = String.format("attachment; filename=\"%s\"", downloadFile.getName()); + downloadFile.delete(); + response.setHeader(headerKey, headerValue); + + OutputStream outStream = response.getOutputStream(); + byte[] buffer = new byte[32 * 1024]; + int bytesRead = -1; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outStream.write(buffer, 0, bytesRead); + } + + inputStream.close(); + outStream.close(); + } + + @RequestMapping(value = { "/portalApi/microservices/parameters" }, method = RequestMethod.POST) + public PortalRestResponse saveWidgetParameter(HttpServletRequest request, HttpServletResponse response, + @RequestBody WidgetCatalogParameter widgetParameters) throws Exception { + EPUser user = EPUserUtils.getUserSession(request); + widgetParameters.setUserId(user.getId()); + try { + WidgetCatalogParameter oldParam = widgetParameterService.getUserParamById(widgetParameters.getWidgetId(), + widgetParameters.getUserId(), widgetParameters.getParamId()); + if (oldParam != null) { + widgetParameters.setId(oldParam.getId()); + } + widgetParameterService.saveUserParameter(widgetParameters); + + } catch (Exception e) { + return new PortalRestResponse(PortalRestStatusEnum.ERROR, "FAILURE", e.getMessage()); + } + return new PortalRestResponse(PortalRestStatusEnum.OK, "SUCCESS", ""); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WidgetsCatalogMarkupController.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WidgetsCatalogMarkupController.java new file mode 100644 index 00000000..78499ef8 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/controller/WidgetsCatalogMarkupController.java @@ -0,0 +1,83 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.controller; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.commons.CommonsMultipartResolver; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.controller.EPUnRestrictedBaseController; +import org.openecomp.portalapp.portal.domain.WidgetServiceHeaders; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.service.ConsulHealthService; + +@SuppressWarnings("unchecked") +@RestController +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class WidgetsCatalogMarkupController extends EPUnRestrictedBaseController { + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(WidgetsCatalogMarkupController.class); + RestTemplate template = new RestTemplate(); + String whatService = "widgets-service"; + + + @Autowired + private ConsulHealthService consulHealthService; + + @Bean + public CommonsMultipartResolver multipartResolver() { + return new CommonsMultipartResolver(); + } + + static { + //for localhost testing only + javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier( + new javax.net.ssl.HostnameVerifier(){ + + public boolean verify(String hostname, + javax.net.ssl.SSLSession sslSession) { + if (hostname.equals("localhost")) { + return true; + } + return false; + } + }); + } + + + @RequestMapping(value = "/portalApi/microservices/markup/{widgetId}", method = RequestMethod.GET) + public String getWidgetMarkup(HttpServletRequest request, HttpServletResponse response, + @PathVariable("widgetId") long widgetId) throws RestClientException, Exception { + return template.getForObject("https://" + consulHealthService.getServiceLocation(whatService) + "/widget/microservices/markup/" + widgetId, String.class, + WidgetServiceHeaders.getInstance()); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AdminUserApp.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AdminUserApp.java new file mode 100644 index 00000000..ecf0b684 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AdminUserApp.java @@ -0,0 +1,88 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class AdminUserApp implements Serializable{ + private static final long serialVersionUID = 1L; + + @Id + @Column(name="USER_ID") + private Long user_Id; + + @Column(name="FIRST_NAME") + private String firstName; + + @Column(name="LAST_NAME") + private String lastName; + + @Column(name="ORG_USER_ID") + private String orgUserId; + + @Id + @Column(name="APP_ID") + private Long appId; + + @Column(name="APP_NAME") + private String appName; + + public Long getAppId() { + return appId; + } + public void setAppId(Long appId) { + this.appId = appId; + } + public Long getUser_Id() { + return user_Id; + } + public void setUserId(Long user_Id) { + this.user_Id = user_Id; + } + 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 getOrgUserId() { + return orgUserId; + } + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } + public String getAppName() { + return appName; + } + public void setAppName(String appName) { + this.appName = appName; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AdminUserApplications.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AdminUserApplications.java new file mode 100644 index 00000000..c90be671 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AdminUserApplications.java @@ -0,0 +1,96 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.util.ArrayList; +import java.util.List; + +public class AdminUserApplications { + private List apps = new ArrayList(); + + private Long user_Id; + private String firstName; + private String lastName; + private String orgUserId; + + public AdminUserApplications(AdminUserApp app) { + setUser_Id(app.getUser_Id()); + setOrgUserId(app.getOrgUserId()); + setFirstName(app.getFirstName()); + setLastName(app.getLastName()); + + addApp(app.getAppId(), app.getAppName()); + } + public Long getUser_Id() { + return user_Id; + } + public void setUser_Id(Long user_Id) { + this.user_Id = user_Id; + } + 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 getOrgUserId() { + return orgUserId; + } + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } + public List getApps() { + return apps; + } + public void setApps(List apps) { + this.apps = apps; + } + public void addApp(Long otherAppId, String otherAppName) { + apps.add(new Application(otherAppId, otherAppName)); + } + + public class Application { + private Long appId; + private String appName; + + public Application(Long otherAppId, String otherAppName) { + setAppId(otherAppId); + setAppName(otherAppName); + } + public Long getAppId() { + return appId; + } + public void setAppId(Long appId) { + this.appId = appId; + } + public String getAppName() { + return appName; + } + public void setAppName(String appName) { + this.appName = appName; + } + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/App.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/App.java new file mode 100644 index 00000000..21068513 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/App.java @@ -0,0 +1,62 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +//@Entity +//@Table(name = "FN_APP") +public class App implements Serializable{ + + private static final long serialVersionUID = 1L; + + //@Id + //@Column(name = "APP_ID") + //@GeneratedValue(strategy=GenerationType.AUTO) + private Long appId; + + //@Column(name = "APP_Name") + private String appName; + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppContactUs.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppContactUs.java new file mode 100644 index 00000000..d399c825 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppContactUs.java @@ -0,0 +1,86 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +/** + * Models rows in the fn_app_contact_us table added for 1610.1. + */ +public class AppContactUs extends DomainVo { + + private static final long serialVersionUID = -2742197830465055134L; + + private EPApp app; + private String description; + private String contactEmail; + private String contactName; + private String url; + private String activeYN; + + public EPApp getApp() { + return app; + } + + public void setApp(EPApp app) { + this.app = app; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getContactEmail() { + return contactEmail; + } + + public void setContactEmail(String contactEmail) { + this.contactEmail = contactEmail; + } + + public String getContactName() { + return contactName; + } + + public void setContactName(String contactName) { + this.contactName = contactName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getActiveYN() { + return activeYN; + } + + public void setActiveYN(String activeYN) { + this.activeYN = activeYN; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppIdAndNameTransportModel.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppIdAndNameTransportModel.java new file mode 100644 index 00000000..1440973e --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppIdAndNameTransportModel.java @@ -0,0 +1,63 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class AppIdAndNameTransportModel implements Serializable{ + private static final long serialVersionUID = 1L; + + @Id + @Column(name="APP_ID") + private Long id; + + @Column(name="APP_NAME") + private String name; + + @Column(name="APP_TYPE") + private Integer appType; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Boolean isRestrictedApp() { + return (this.appType == 2 ? true : false); + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppsResponse.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppsResponse.java new file mode 100644 index 00000000..4935a22c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/AppsResponse.java @@ -0,0 +1,76 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +public class AppsResponse { + + public AppsResponse(Long id, String name, Boolean restrictedApp, Boolean enabled) { + this.index = id; + this.title = this.value = name; + this.restrictedApp = restrictedApp; + this.enabled = enabled; + } + + public Long getIndex() { + return index; + } + + public void setIndex(Long index) { + this.index = index; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + // Hide the implementation of restricted and normal app from the front end. + // The json sent and received will include restrictedApp but not appType. + + public void setRestrictedApp(Boolean restrictedApp) { + this.restrictedApp = restrictedApp; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + private Long index; + private String title; + private String value; + public Boolean restrictedApp; + public Boolean enabled; + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/BEProperty.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/BEProperty.java new file mode 100644 index 00000000..e877641b --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/BEProperty.java @@ -0,0 +1,43 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +public class BEProperty { + private String key; + private String value; + + public BEProperty(String key, String value){ + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + public void setKey(String key) { + this.key = key; + } + public String getValue() { + return value; + } + public void setValue(String value) { + this.value = value; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/BasicAuthCredentials.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/BasicAuthCredentials.java new file mode 100644 index 00000000..359945ce --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/BasicAuthCredentials.java @@ -0,0 +1,95 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.util.List; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class BasicAuthCredentials extends DomainVo { + + private static final long serialVersionUID = 1L; + + public BasicAuthCredentials() { + + } + + private Long id; + private String applicationName; + private String username; + private String password; + private String isActive; + private List endpoints; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getApplicationName() { + return applicationName; + } + + public void setApplicationName(String applicationName) { + this.applicationName = applicationName; + } + + 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 String getIsActive() { + return isActive; + } + + public void setIsActive(String isActive) { + this.isActive = isActive; + } + + public List getEndpoints() { + return endpoints; + } + + public void setEndpoints(List endpoints) { + this.endpoints = endpoints; + } + + @Override + public String toString() { + return "BasicAuthCredentials [id=" + id + ", applicationName=" + applicationName + ", username=" + username + + ", password=" + password + ", isActive=" + isActive + "]"; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPApp.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPApp.java new file mode 100644 index 00000000..1ba9e2d7 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPApp.java @@ -0,0 +1,292 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import javax.persistence.Lob; + +import org.apache.commons.lang.StringUtils; +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +/** + * Model for all columns in the fn_app table. + */ +public class EPApp extends DomainVo { + + private static final long serialVersionUID = 1L; + + private String name; + private String imageUrl; + private String description; + private String notes; + private String url; + private String alternateUrl; + private String appRestEndpoint; + private String mlAppName; + private String mlAppAdminId; + private Long motsId; + private String username; + private String appPassword; + @Lob + private byte[] thumbnail; + private Boolean open; + private Boolean enabled; + private String uebTopicName; + private String uebKey; + private String uebSecret; + private Integer appType; + + private AppContactUs contactUs; + + public EPApp() { + // Attention!!! + // We set here all default values. We also place protection + // into setters for fields with default values. + // If we don't use such protection we are able to place null + // to these fields and save such fields into DB even if DB has + // default values for these fields. + this.name = ""; + this.mlAppName = ""; + this.mlAppAdminId = ""; + this.username = ""; + this.appPassword = ""; + this.open = new Boolean(false); + this.enabled = new Boolean(true); + this.uebTopicName = ""; + this.uebKey = ""; + this.uebSecret = ""; + this.appType = 1; + } + + public String getName() { + return name; + } + + public void setName(String name) { + if (StringUtils.isEmpty(name)) { + name = ""; + } + this.name = name; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public byte[] getThumbnail() { + return this.thumbnail; + } + + public void setThumbnail(byte[] thumbnail) { + this.thumbnail = thumbnail; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getAlternateUrl() { + return alternateUrl; + } + + public void setAlternateUrl(String alternateUrl) { + this.alternateUrl = alternateUrl; + } + + public String getAppRestEndpoint() { + return appRestEndpoint; + } + + public void setAppRestEndpoint(String appRestEndpoint) { + this.appRestEndpoint = appRestEndpoint; + } + + public String getMlAppName() { + return mlAppName; + } + + public void setMlAppName(String mlAppName) { + if (StringUtils.isEmpty(mlAppName)) { + mlAppName = ""; + } + this.mlAppName = mlAppName; + } + + public String getMlAppAdminId() { + return mlAppAdminId; + } + + public void setMlAppAdminId(String mlAppAdminId) { + if (StringUtils.isEmpty(mlAppAdminId)) { + mlAppAdminId = ""; + } + this.mlAppAdminId = mlAppAdminId; + } + + public Long getMotsId() { + return motsId; + } + + public void setMotsId(Long motsId) { + this.motsId = motsId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAppPassword() { + return appPassword; + } + + public void setAppPassword(String appPassword) { + if (StringUtils.isEmpty(appPassword)) { + appPassword = ""; + } + this.appPassword = appPassword; + } + + public Boolean getOpen() { + return open; + } + + public void setOpen(Boolean open) { + if (open == null) { + open = new Boolean(false); + } + this.open = open; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + if (enabled == null) { + enabled = new Boolean(true); + } + this.enabled = enabled; + } + + public Integer getAppType() { + return appType; + } + + public void setAppType(Integer appType) { + if (appType == null) { + appType = new Integer(1); + } + this.appType = appType; + } + + public void setRestrictedApp(Boolean restrictedApp) { + Integer result = 1; + if (restrictedApp) { + result = 2; + } + this.appType = result; + } + + public Boolean isRestrictedApp() { + return (this.appType == 2 ? true : false); + } + + public int compareTo(Object obj) { + Long c1 = getId(); + Long c2 = ((EPApp) obj).getId(); + + return c1.compareTo(c2); + } + + public String getUebTopicName() { + return this.uebTopicName; + } + + public void setUebTopicName(String topicName) { + if (StringUtils.isEmpty(topicName)) { + this.uebTopicName = ""; + } + this.uebTopicName = topicName; + } + + public String getUebKey() { + return this.uebKey; + } + + public void setUebKey(String uebKey) { + if (StringUtils.isEmpty(uebKey)) { + this.uebKey = ""; + } + this.uebKey = uebKey; + } + + public String getUebSecret() { + return this.uebSecret; + } + + public void setUebSecret(String uebSecret) { + if (StringUtils.isEmpty(uebSecret)) { + this.uebSecret = ""; + } + this.uebSecret = uebSecret; + } + + public AppContactUs getContactUs() { + return contactUs; + } + + public void setContactUs(AppContactUs contactUs) { + this.contactUs = contactUs; + } + + @Override + public String toString() { + String str = "[" + getId() + ":" + getName() + "]"; + return str; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPEndpoint.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPEndpoint.java new file mode 100644 index 00000000..85e390e6 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPEndpoint.java @@ -0,0 +1,37 @@ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class EPEndpoint extends DomainVo { + + private static final long serialVersionUID = 1L; + + public EPEndpoint() { + + } + + private Long id; + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "EPEndpoint [id=" + id + ", name=" + name + "]"; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPEndpointAccount.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPEndpointAccount.java new file mode 100644 index 00000000..3d7dbd1f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPEndpointAccount.java @@ -0,0 +1,46 @@ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class EPEndpointAccount extends DomainVo { + + private static final long serialVersionUID = 1L; + + public EPEndpointAccount() { + + } + + private Long id; + private Long ep_id; + private Long account_id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getEp_id() { + return ep_id; + } + + public void setEp_id(Long ep_id) { + this.ep_id = ep_id; + } + + public Long getAccount_id() { + return account_id; + } + + public void setAccount_id(Long account_id) { + this.account_id = account_id; + } + + @Override + public String toString() { + return "EPEndpointAccount [id=" + id + ", ep_id=" + ep_id + ", account_id=" + account_id + "]"; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPRole.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPRole.java new file mode 100644 index 00000000..ea70982a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPRole.java @@ -0,0 +1,202 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.openecomp.portalsdk.core.domain.RoleFunction; +import org.openecomp.portalsdk.core.domain.support.DomainVo; +import com.fasterxml.jackson.annotation.JsonIgnore; + +public class EPRole extends DomainVo { + + private static final long serialVersionUID = 1L; + private String name; + private boolean active; + private Integer priority; + + // ECOMP will identify the specific remote application role id by appID;appRoleId among all the application roles it persists. + private Long appId; // used by ECOMP only + private Long appRoleId; // used by ECOMP only + + private SortedSet roleFunctions = new TreeSet(); + + private SortedSet childRoles = new TreeSet(); + + @JsonIgnore + private SortedSet parentRoles = new TreeSet(); + + public EPRole() {} + + public String getName() { + return name; + } + + public boolean getActive() { + return active; + } + + public SortedSet getRoleFunctions() { + return roleFunctions; + } + + public Integer getPriority() { + return priority; + } + + public SortedSet getChildRoles() { + return childRoles; + } + + public SortedSet getParentRoles() { + return parentRoles; + } + + public void setName(String name) { + this.name = name; + } + + public void setActive(boolean active) { + this.active = active; + } + + public void setRoleFunctions(SortedSet roleFunctions) { + this.roleFunctions = roleFunctions; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + public void setChildRoles(SortedSet childRoles) { + this.childRoles = childRoles; + } + + public void setParentRoles(SortedSet parentRoles) { + this.parentRoles = parentRoles; + } + + public void addRoleFunction(RoleFunction roleFunction) { + this.roleFunctions.add(roleFunction); + } + + public void addChildRole(EPRole role) { + this.childRoles.add(role); + } + + public void addParentRole(EPRole role) { + this.parentRoles.add(role); + } + + public String getEditUrl() { + return "/role.htm?role_id=" + getId(); + } + + public String getToggleActiveImage() { + return "/static/fusion/images/" + (getActive() ? "active.png" : "inactive.png" ); + } + + public String getToggleActiveAltText() { + return getActive() ? "Click to Deactivate Role" : "Click to Activate Role"; + } + + public void removeChildRole(Long roleId) { + Iterator i = this.childRoles.iterator(); + + while (i.hasNext()) { + EPRole childRole = (EPRole)i.next(); + if (childRole.getId().equals(roleId)) { + this.childRoles.remove(childRole); + break; + } + } + } + + public void removeParentRole(Long roleId) { + Iterator i = this.parentRoles.iterator(); + + while (i.hasNext()) { + EPRole parentRole = (EPRole)i.next(); + if (parentRole.getId().equals(roleId)) { + this.parentRoles.remove(parentRole); + break; + } + } + } + + public void removeRoleFunction(String roleFunctionCd) { + Iterator i = this.roleFunctions.iterator(); + + while (i.hasNext()) { + RoleFunction roleFunction = (RoleFunction)i.next(); + if (roleFunction.getCode().equals(roleFunctionCd)) { + this.roleFunctions.remove(roleFunction); + break; + } + } + } + + public int compareTo(Object obj){ + EPRole other = (EPRole)obj; + + if(this.appId == null) + if(other.getAppId() == null) + return compareByName(other); //equal + else + return -1; + else // this.appId != null + if(other.getAppId() == null) + return 1; // appId != null, but others is null + else{ + int appIdCompareResult = appId.compareTo(other.getAppId()); + return appIdCompareResult == 0? compareByName(other) : appIdCompareResult; + } + } + + private int compareByName(EPRole other) { + String c1 = getName(); + String c2 = other.getName(); + + return (c1 == null || c2 == null) ? 1 : c1.compareTo(c2); + } + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public Long getAppRoleId() { + return appRoleId; + } + + public void setAppRoleId(Long appRoleId) { + this.appRoleId = appRoleId; + } + + @Override + public String toString() { + return "[Id = " + id + ", name = " + name + "]"; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUser.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUser.java new file mode 100644 index 00000000..3a52ffc3 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUser.java @@ -0,0 +1,650 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.util.Date; +import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.openecomp.portalapp.portal.utils.PortalConstants; +import org.openecomp.portalsdk.core.domain.User; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +public class EPUser extends User { + + private Long orgId; + private Long managerId; + private String firstName; + private String middleInitial; + private String lastName; + private String phone; + private String fax; + private String cellular; + private String email; + private Long addressId; + private String alertMethodCd; + private String hrid; + private String orgUserId; + private String orgCode; + private String address1; + private String address2; + private String city; + private String state; + private String zipCode; + private String country; + private String orgManagerUserId; + private String locationClli; + private String businessCountryCode; + private String businessCountryName; + private String businessUnit; + private String businessUnitName; + private String department; + private String departmentName; + private String companyCode; + private String company; + private String zipCodeSuffix; + private String jobTitle; + private String commandChain; + private String siloStatus; + private String costCenter; + private String financialLocCode; + + + + private String loginId; + private String loginPwd; + private Date lastLoginDate; + private boolean active; + private boolean internal; + private Long selectedProfileId; + private Long timeZoneId; + private boolean online; + private String chatId; + + private static final long serialVersionUID = 1L; + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPUser.class); + private static final String ECOMP_PORTAL_NAME = "ECOMP"; + private boolean isGuest = false; + + private SortedSet userApps = new TreeSet(); + private SortedSet pseudoRoles = new TreeSet(); + + public EPUser() {} + + public Long getAddressId() { + return addressId; + } + + public String getAlertMethodCd() { + return alertMethodCd; + } + + public String getCellular() { + return cellular; + } + + public String getEmail() { + return email; + } + + public String getFax() { + return fax; + } + + public String getFirstName() { + return firstName; + } + + public String getHrid() { + return hrid; + } + + public Date getLastLoginDate() { + return lastLoginDate; + } + + public String getLastName() { + return lastName; + } + + @JsonIgnore + public String getFullName() { + return getFirstName() + " " + getLastName(); + } + + public String getLoginId() { + return loginId; + } + + public String getLoginPwd() { + return loginPwd; + } + + public Long getManagerId() { + return managerId; + } + + public String getMiddleInitial() { + return middleInitial; + } + + public String getOrgCode() { + return orgCode; + } + + public Long getOrgId() { + return orgId; + } + + public String getPhone() { + return phone; + } + + public String getOrgUserId() { + return orgUserId; + } + + public boolean getActive() { + return active; + } + + public boolean getInternal() { + return internal; + } + + public String getAddress1() { + return address1; + } + + public String getAddress2() { + return address2; + } + + public String getCity() { + return city; + } + + public String getCountry() { + return country; + } + + public String getState() { + return state; + } + + public String getZipCode() { + return zipCode; + } + + public String getBusinessCountryCode() { + return businessCountryCode; + } + + public String getCommandChain() { + return commandChain; + } + + public String getCompany() { + return company; + } + + public String getCompanyCode() { + return companyCode; + } + + public String getDepartment() { + return department; + } + + public String getJobTitle() { + return jobTitle; + } + + public String getLocationClli() { + return locationClli; + } + + public String getOrgManagerUserId() { + return orgManagerUserId; + } + + public String getZipCodeSuffix() { + return zipCodeSuffix; + } + + public String getBusinessCountryName() { + return businessCountryName; + } + + public Long getSelectedProfileId() { + return selectedProfileId; + } + + public void setAddressId(Long addressId) { + this.addressId = addressId; + } + + public void setAlertMethodCd(String alertMethodCd) { + this.alertMethodCd = alertMethodCd; + } + + public void setCellular(String cellular) { + this.cellular = cellular; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setFax(String fax) { + this.fax = fax; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public void setHrid(String hrid) { + this.hrid = hrid; + } + + public void setLastLoginDate(Date lastLoginDate) { + this.lastLoginDate = lastLoginDate; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public void setLoginId(String loginId) { + this.loginId = loginId; + } + + public void setLoginPwd(String loginPwd) { + this.loginPwd = loginPwd; + } + + public void setManagerId(Long managerId) { + this.managerId = managerId; + } + + public void setMiddleInitial(String middleInitial) { + this.middleInitial = middleInitial; + } + + public void setOrgCode(String orgCode) { + this.orgCode = orgCode; + } + + public void setOrgId(Long orgId) { + this.orgId = orgId; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } + + public void setActive(boolean active) { + this.active = active; + } + + public void setInternal(boolean internal) { + this.internal = internal; + } + + public void setAddress1(String address1) { + this.address1 = address1; + } + + public void setAddress2(String address2) { + this.address2 = address2; + } + + public void setCity(String city) { + this.city = city; + } + + public void setCountry(String country) { + this.country = country; + } + + public void setState(String state) { + this.state = state; + } + + public void setZipCode(String zipCode) { + this.zipCode = zipCode; + } + + public void setBusinessCountryCode(String businessCountryCode) { + this.businessCountryCode = businessCountryCode; + } + + public void setCommandChain(String commandChain) { + this.commandChain = commandChain; + } + + public void setCompany(String company) { + this.company = company; + } + + public void setCompanyCode(String companyCode) { + this.companyCode = companyCode; + } + + public void setDepartment(String department) { + this.department = department; + } + + public void setJobTitle(String jobTitle) { + this.jobTitle = jobTitle; + } + + public void setLocationClli(String locationClli) { + this.locationClli = locationClli; + } + + public void setOrgManagerUserId(String orgManagerUserId) { + this.orgManagerUserId = orgManagerUserId; + } + + public void setZipCodeSuffix(String zipCodeSuffix) { + this.zipCodeSuffix = zipCodeSuffix; + } + + public void setBusinessCountryName(String businessCountryName) { + this.businessCountryName = businessCountryName; + } + + public SortedSet getPseudoRoles() { + return pseudoRoles; + } + + public void setPseudoRoles(SortedSet pseudoRoles) { + this.pseudoRoles = pseudoRoles; + } + + public void setSelectedProfileId(Long selectedProfileId) { + this.selectedProfileId = selectedProfileId; + } + + public Long getTimeZoneId() { + return timeZoneId; + } + + public void setTimeZoneId(Long timeZoneId) { + this.timeZoneId = timeZoneId; + } + + public String getBusinessUnit() { + return businessUnit; + } + + public void setBusinessUnit(String businessUnit) { + this.businessUnit = businessUnit; + } + + public String getSiloStatus() { + return siloStatus; + } + + public void setSiloStatus(String siloStatus) { + this.siloStatus = siloStatus; + } + + public String getCostCenter() { + return costCenter; + } + + public void setCostCenter(String costCenter) { + this.costCenter = costCenter; + } + + public String getFinancialLocCode() { + return financialLocCode; + } + + public void setFinancialLocCode(String financialLocCode) { + this.financialLocCode = financialLocCode; + } + + public String getBusinessUnitName() { + return businessUnitName; + } + + public void setBusinessUnitName(String businessUnitName) { + this.businessUnitName = businessUnitName; + } + + public String getDepartmentName() { + return departmentName; + } + + public void setDepartmentName(String departmentName) { + this.departmentName = departmentName; + } + + public int compareTo(Object obj){ + EPUser user = (EPUser)obj; + + String c1 = getLastName() + getFirstName() + getMiddleInitial(); + String c2 = user.getLastName() + user.getFirstName() + user.getMiddleInitial(); + + return c1.compareTo(c2); + } + + public boolean isOnline() { + return online; + } + + public void setOnline(boolean online) { + this.online = online; + } + + public String getChatId() { + return chatId; + } + + public void setChatId(String chatId) { + this.chatId = chatId; + } + + + public void setPseudoEPRoles(SortedSet pseudoRoles) { + this.pseudoRoles = pseudoRoles; + } + + public SortedSet getEPUserApps() { + return userApps; + } + + public void setEPUserApps(SortedSet userApps) { + this.userApps = userApps; + } + + public void addUserApp(EPUserApp userApp) { + userApps.add(userApp); + } + + public void addAppRoles(EPApp app, SortedSet roles) { + if (roles != null) { + // add all + SortedSet userApps = new TreeSet(); + // this.userApps.removeAll(this.userApps); + Iterator itr = roles.iterator(); + while (itr.hasNext()) { + EPRole role = (EPRole) itr.next(); + EPUserApp userApp = new EPUserApp(); + userApp.setUserId(this.id); + userApp.setApp(app); + userApp.setRole(role); + userApps.add(userApp); + } + setEPUserApps(userApps); + } else { + // remove all + setEPUserApps(null); + } + + } + + public SortedSet getAppEPRoles(EPApp app) { + + logger.debug(EELFLoggerDelegate.debugLogger, "In EPUser.getAppEPRoles() - app = {}", app.getName()); + + SortedSet roles = new TreeSet(); + SortedSet userAppRoles = getEPUserApps(); + + logger.debug(EELFLoggerDelegate.debugLogger, "In EPUser.getAppEPRoles() - userApps = ", userAppRoles.size()); + + Iterator userAppRolesIterator = userAppRoles.iterator(); + EPUserApp userAppRole = null; + // getting default app + while (userAppRolesIterator.hasNext()) { + EPUserApp tempUserApp = (EPUserApp) userAppRolesIterator.next(); + if (tempUserApp.getApp().getId().equals(app.getId())) { + + logger.debug(EELFLoggerDelegate.debugLogger, "In EPUser.getAppEPRoles() - for user {}, found application {}", this.getFullName(), app.getName()); + + userAppRole = tempUserApp; + + EPRole role = userAppRole.getRole(); + if(role.getActive()){ + logger.debug(EELFLoggerDelegate.debugLogger, "In EPUser.getAppEPRoles() - Role {} is active - adding for user {} and app {}", role.getName(), this.getFullName(), app.getName()); + roles.add((EPRole) role); + } + else{ + logger.debug(EELFLoggerDelegate.debugLogger, "In EPUser.getAppEPRoles() - Role {} is NOT active - NOT adding for user {} and app {}", role.getName(), this.getFullName(), app.getName()); + } + } + } + logger.debug(EELFLoggerDelegate.debugLogger, "In EPUser.getAppEPRoles() - roles = ", roles.size()); + + return roles; + } + + + /** + * Attention! Not for use in ECOMP + */ + public SortedSet getAppRoles(EPApp app) { + SortedSet roles = new TreeSet(); + SortedSet apps = getEPUserApps(); + Iterator appsItr = apps.iterator(); + EPUserApp userApp = null; + // getting default app + while (appsItr.hasNext()) { + EPUserApp tempUserApp = (EPUserApp) appsItr.next(); + if (tempUserApp.getApp().getId().equals(app.getId())) { + userApp = tempUserApp; + roles.add((EPRole) userApp.getRole()); + } + } + return roles; + } + + + + /** + * Attention! Not for use in ECOMP + */ + public SortedSet getEPRoles() { + EPApp app = new EPApp(); + app.setId(new Long(PortalConstants.PORTAL_APP_ID)); + app.setName(ECOMP_PORTAL_NAME); + return getAppEPRoles(app); + } + + /** + * Attention! Not for use in ECOMP + */ + public void setEPRoles(SortedSet roles) { + EPApp app = new EPApp(); + app.setId(new Long(PortalConstants.PORTAL_APP_ID)); + app.setName(ECOMP_PORTAL_NAME); + addAppRoles(app, roles); + } + + /** + * Attention! Not for use in ECOMP + */ + public void removeEPRole(Long roleId) { + SortedSet apps = getEPUserApps(); + Iterator appsItr = apps.iterator(); + // getting default app + while (appsItr.hasNext()) { + EPUserApp tempUserApp = (EPUserApp) appsItr.next(); + if (tempUserApp.equals(new Long(PortalConstants.PORTAL_APP_ID)) && tempUserApp.getRole().getId().equals(roleId)) { + appsItr.remove(); + } + } + } + + /** + * Attention! Not for use in ECOMP + */ + public void addEPRole(EPRole role) { + if (role != null) { + SortedSet roles = getEPRoles(); + if (roles == null) { + roles = new TreeSet(); + } + roles.add(role); + setEPRoles(roles); + } + } + + public boolean isGuest() { + return isGuest; + } + + public void setGuest(boolean isGuest) { + this.isGuest = isGuest; + } + + @Override + public String toString() { + return "EPUser [orgId=" + orgId + ", managerId=" + managerId + ", firstName=" + firstName + ", middleInitial=" + + middleInitial + ", lastName=" + lastName + ", phone=" + phone + ", fax=" + fax + ", cellular=" + + cellular + ", email=" + email + ", addressId=" + addressId + ", alertMethodCd=" + alertMethodCd + + ", hrid=" + hrid + ", orgUserId=" + orgUserId + ", orgCode=" + orgCode + ", address1=" + address1 + + ", address2=" + address2 + ", city=" + city + ", state=" + state + ", zipCode=" + zipCode + + ", country=" + country + ", orgManagerUserId=" + orgManagerUserId + ", locationClli=" + locationClli + + ", businessCountryCode=" + businessCountryCode + ", businessCountryName=" + businessCountryName + + ", businessUnit=" + businessUnit + ", businessUnitName=" + businessUnitName + ", department=" + + department + ", departmentName=" + departmentName + ", companyCode=" + companyCode + ", company=" + + company + ", zipCodeSuffix=" + zipCodeSuffix + ", jobTitle=" + jobTitle + ", commandChain=" + + commandChain + ", siloStatus=" + siloStatus + ", costCenter=" + costCenter + ", financialLocCode=" + + financialLocCode + ", loginId=" + loginId + ", loginPwd=" + loginPwd + ", lastLoginDate=" + + lastLoginDate + ", active=" + active + ", internal=" + internal + ", selectedProfileId=" + + selectedProfileId + ", timeZoneId=" + timeZoneId + ", online=" + online + ", chatId=" + chatId + + ", isGuest=" + isGuest + ", userApps=" + userApps + ", pseudoRoles=" + pseudoRoles + "]"; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserApp.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserApp.java new file mode 100644 index 00000000..9ed62d8b --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserApp.java @@ -0,0 +1,119 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + + +@SuppressWarnings("rawtypes") +public class EPUserApp implements java.io.Serializable, Comparable { + + private static final long serialVersionUID = 1L; + + private Long userId; + private EPApp app; + private EPRole role; + private Short priority; + + public EPUserApp() { + } + + public Long getAppId() { + return this.getApp().getId(); + } + + public Long getRoleId() { + return (role == null) ? null : role.getId(); + } + + public Long getAppRoleId() { + return (role.getAppRoleId() == null) ? null : role.getAppRoleId(); + } + + @Override + public String toString() { + String str = "[u: "+getUserId()+"; a: "+getAppId()+", r: "+getRoleId()+"; appRoleId: "+getAppRoleId()+"]"; + return str; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long id) { + this.userId = id; + } + + public EPApp getApp() { + return app; + } + + public void setApp(EPApp app) { + this.app = app; + } + + public EPRole getRole() { + return role; + } + + public void setRole(EPRole role) { + this.role = role; + } + + public Short getPriority() { + return this.priority; + } + + public void setPriority(Short priority) { + this.priority = priority; + } + + public boolean equals(Object other) { + if ((this == other)) + return true; + if ((other == null)) + return false; + if (!(other instanceof EPUserApp)) + return false; + EPUserApp castOther = (EPUserApp) other; + + return (this.getUserId().equals(castOther.getUserId())) + && (this.getApp().getId().equals(castOther.getApp().getId())) + && (this.getRole().getId().equals(castOther.getRole().getId())) + && ((this.priority==null && castOther.getPriority()==null) || this.getPriority().equals(castOther.getPriority())); + } + + public int hashCode() { + int result = 17; + + result = 37 * result + (int) (this.getUserId()==null ? 0 : this.getUserId().intValue()); + result = 37 * result + (int) (this.getApp().getId()==null ? 0 : this.getApp().getId().intValue()); + result = 37 * result + (int) (this.getRole().getId()==null ? 0 : this.getRole().getId().intValue()); + result = 37 * result + (int) (this.priority==null ? 0 : this.priority); + return result; + } + + public int compareTo(Object other){ + EPUserApp castOther = (EPUserApp) other; + + Long c1 = (this.getUserId()==null ? 0 : this.getUserId()) + (this.getApp()==null||this.getApp().getId()==null ? 0 : this.getApp().getId()) + (this.getRole()==null||this.getRole().getId()==null ? 0 : this.getRole().getId()) + (this.priority==null ? 0 : this.priority); + Long c2 = (castOther.getUserId()==null ? 0 : castOther.getUserId()) + (castOther.getApp()==null||castOther.getApp().getId()==null ? 0 : castOther.getApp().getId()) + (castOther.getRole()==null||castOther.getRole().getId()==null ? 0 : castOther.getRole().getId()) + (castOther.priority==null ? 0 : castOther.priority); + + return c1.compareTo(c2); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppCatalogRoles.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppCatalogRoles.java new file mode 100644 index 00000000..89411ea5 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppCatalogRoles.java @@ -0,0 +1,92 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@Entity +@JsonInclude +public class EPUserAppCatalogRoles extends DomainVo { + + private static final long serialVersionUID = -5259869298825093816L; + + + @Id + @Column(name="requested_role_id") + public Long requestedRoleId; + + + @Id + @Column(name="role_name") + public String rolename; + + @Id + @Column(name="request_status") + public String requestStatus; + + @Id + @Column(name="app_id") + public Long appId; + + public Long getRequestedRoleId() { + return requestedRoleId; + } + + public void setRequestedRoleId(Long requestedRoleId) { + this.requestedRoleId = requestedRoleId; + } + + public String getRolename() { + return rolename; + } + + public void setRolename(String rolename) { + this.rolename = rolename; + } + + public String getRequestStatus() { + return requestStatus; + } + + public void setRequestStatus(String requestStatus) { + this.requestStatus = requestStatus; + } + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public String toString() { + return "EPUserAppCatalogRoles [requestedRoleId=" + requestedRoleId + ", rolename=" + rolename + + ", requestStatus=" + requestStatus + ", appId=" + appId + "]"; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppRolesRequest.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppRolesRequest.java new file mode 100644 index 00000000..1d73945e --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppRolesRequest.java @@ -0,0 +1,87 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + + +import java.util.Date; +import java.util.Set; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + + +public class EPUserAppRolesRequest extends DomainVo { + + private static final long serialVersionUID = -7225288307806389019L; + private Long userId; + private Long appId; + private Date createdDate; + private Date updatedDate; + private String requestStatus; + + private Set epRequestIdDetail; + + public final Long getUserId() { + return userId; + } + + public final void setUserId(Long userId) { + this.userId = userId; + } + + public final Long getAppId() { + return appId; + } + + public final void setAppId(Long appId) { + this.appId = appId; + } + + public final Date getCreatedDate() { + return createdDate; + } + + public final void setCreatedDate(Date createdDate) { + this.createdDate = createdDate; + } + + public final Date getUpdatedDate() { + return updatedDate; + } + + public final void setUpdatedDate(Date updatedDate) { + this.updatedDate = updatedDate; + } + + public final String getRequestStatus() { + return requestStatus; + } + + public final void setRequestStatus(String requestStatus) { + this.requestStatus = requestStatus; + } + + public final Set getEpRequestIdDetail() { + return epRequestIdDetail; + } + + public final void setEpRequestIdDetail(Set epMyLoginsDetail) { + this.epRequestIdDetail = epMyLoginsDetail; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppRolesRequestDetail.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppRolesRequestDetail.java new file mode 100644 index 00000000..d7989bcd --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppRolesRequestDetail.java @@ -0,0 +1,52 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class EPUserAppRolesRequestDetail extends DomainVo{ + + private static final long serialVersionUID = -4908856671135348157L; + + private Long reqRoleId; + private String reqType; + private EPUserAppRolesRequest epRequestIdData; + + public final EPUserAppRolesRequest getEpRequestIdData() { + return epRequestIdData; + } + public final void setEpRequestIdData(EPUserAppRolesRequest epRequestIdData) { + this.epRequestIdData = epRequestIdData; + } + public final Long getReqRoleId() { + return reqRoleId; + } + public final void setReqRoleId(Long reqRoleId) { + this.reqRoleId = reqRoleId; + } + public final String getReqType() { + return reqType; + } + public final void setReqType(String reqType) { + this.reqType = reqType; + } + +} + diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppsManualSortPreference.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppsManualSortPreference.java new file mode 100644 index 00000000..30d69bab --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppsManualSortPreference.java @@ -0,0 +1,50 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class EPUserAppsManualSortPreference extends DomainVo { + + private static final long serialVersionUID = -6270136291139278216L; + private int userId; + private Long appId; + private int appManualSortOrder; + + public int getUserId() { + return userId; + } + public void setUserId(int userId) { + this.userId = userId; + } + public Long getAppId() { + return appId; + } + public void setAppId(Long appId) { + this.appId = appId; + } + public int getAppManualSortOrder() { + return appManualSortOrder; + } + public void setAppManualSortOrder(int appManualSortOrder) { + this.appManualSortOrder = appManualSortOrder; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppsSortPreference.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppsSortPreference.java new file mode 100644 index 00000000..1cab30ff --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserAppsSortPreference.java @@ -0,0 +1,44 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class EPUserAppsSortPreference extends DomainVo{ + + private static final long serialVersionUID = -5267000174894006433L; + + private int userId; + private String sortPref; + + public int getUserId() { + return userId; + } + public void setUserId(final int userId) { + this.userId = userId; + } + public String getSortPref() { + return sortPref; + } + public void setSortPref(final String sortPref) { + this.sortPref = sortPref; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserNotification.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserNotification.java new file mode 100644 index 00000000..c82db4de --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPUserNotification.java @@ -0,0 +1,57 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.util.Date; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + + +public class EPUserNotification extends DomainVo { + private Long userId; + private Long notificationId; + private String viewed; + private Date updateTime; + + public Long getUserId() { + return userId; + } + public void setUserId(Long userId) { + this.userId = userId; + } + public Long getNotificationId() { + return notificationId; + } + public void setNotificationId(Long notificationId) { + this.notificationId = notificationId; + } + public String getViewed() { + return viewed; + } + public void setViewed(String viewed) { + this.viewed = viewed; + } + public Date getUpdateTime() { + return updateTime; + } + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPWidgetsManualSortPreference.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPWidgetsManualSortPreference.java new file mode 100644 index 00000000..4a3b784b --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EPWidgetsManualSortPreference.java @@ -0,0 +1,70 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class EPWidgetsManualSortPreference extends DomainVo{ + + private static final long serialVersionUID = 4607102334801223570L; + private int userId; + private Long widgetId; + private int widgetRow; + private int widgetCol; + private int widgetWidth; + private int widgetHeight; + + public int getUserId() { + return userId; + } + public void setUserId(int userId) { + this.userId = userId; + } + public Long getWidgetId() { + return widgetId; + } + public void setWidgetId(Long widgetId) { + this.widgetId = widgetId; + } + public int getWidgetRow() { + return widgetRow; + } + public void setWidgetRow(int widgetRow) { + this.widgetRow = widgetRow; + } + public int getWidgetCol() { + return widgetCol; + } + public void setWidgetCol(int widgetCol) { + this.widgetCol = widgetCol; + } + public int getWidgetWidth() { + return widgetWidth; + } + public void setWidgetWidth(int widgetWidth) { + this.widgetWidth = widgetWidth; + } + public int getWidgetHeight() { + return widgetHeight; + } + public void setWidgetHeight(int widgetHeight) { + this.widgetHeight = widgetHeight; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompApp.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompApp.java new file mode 100644 index 00000000..1cf89fbd --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompApp.java @@ -0,0 +1,146 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +/** + * Model for a subset of the columns in the fn_app table. + */ +public class EcompApp { + + protected Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private String imageUrl; + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + private String description; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + private String notes; + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + private String url; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + private String alternateUrl; + + public String getAlternateUrl() { + return alternateUrl; + } + + public void setAlternateUrl(String alternateUrl) { + this.alternateUrl = alternateUrl; + } + + private String uebTopicName; + + public String getUebTopicName() { + return uebTopicName; + } + + public void setUebTopicName(String topicName) { + this.uebTopicName = topicName; + } + + private String uebKey; + + public String getUebKey() { + return uebKey; + } + + public void setUebKey(String uebKey) { + this.uebKey = uebKey; + } + + private String uebSecret; + + public String getUebSecret() { + return uebSecret; + } + + public void setUebSecret(String secret) { + this.uebSecret = secret; + } + + private Boolean enabled; + + public Boolean isEnabled() { + return enabled; + } + + public void setEnabled(Boolean enable) { + this.enabled = enable; + } + + private Boolean restrictedApp; + + public Boolean isRestrictedApp() { + return restrictedApp; + } + + public void setRestrictedApp(Boolean restrictedApp) { + this.restrictedApp = restrictedApp; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAppRole.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAppRole.java new file mode 100644 index 00000000..78cf4162 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAppRole.java @@ -0,0 +1,80 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; + +@Entity +@IdClass(EcompAppRoleId.class) +public class EcompAppRole { + + @Id + @Column(name="app_id") + private Long appId; + + @Id + @Column(name="app_name") + private String appName; + + @Id + @Column(name="role_id") + private Integer roleId; + + @Id + @Column(name="role_name") + private String roleName; + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public Integer getRoleId() { + return roleId; + } + + public void setRoleId(Integer roleId) { + this.roleId = roleId; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAppRoleId.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAppRoleId.java new file mode 100644 index 00000000..d89c0736 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAppRoleId.java @@ -0,0 +1,83 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Id; + +public class EcompAppRoleId implements Serializable{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + @Id + @Column(name="app_id") + private Long appId; + + @Id + @Column(name="app_name") + private String appName; + + @Id + @Column(name="role_id") + private Integer roleId; + + @Id + @Column(name="role_name") + private String roleName; + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public Integer getRoleId() { + return roleId; + } + + public void setRoleId(Integer roleId) { + this.roleId = roleId; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAuditLog.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAuditLog.java new file mode 100644 index 00000000..4d4ec277 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EcompAuditLog.java @@ -0,0 +1,61 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.util.Date; + +import org.openecomp.portalsdk.core.domain.AuditLog; + +public class EcompAuditLog extends AuditLog { + + private static final long serialVersionUID = 7970458389782626231L; + + // values of activity_cd column in table fu_lu_activity + // as loaded by DML sql script + public static final String CD_ACTIVITY_ADD_CHILD_ROLE = "add_child_role"; + public static final String CD_ACTIVITY_ADD_ROLE = "add_role"; + public static final String CD_ACTIVITY_ADD_ROLE_FUNCTION = "add_role_function"; + public static final String CD_ACTIVITY_ADD_USER_ROLE = "add_user_role"; + public static final String CD_ACTIVITY_APP_ACCESS = "app_access"; + public static final String CD_ACTIVITY_FUNCTIONAL_ACCESS = "functional_access"; + public static final String CD_ACTIVITY_GUEST_LOGIN = "guest_login"; + public static final String CD_ACTIVITY_LOGIN = "login"; + public static final String CD_ACTIVITY_LOGOUT = "logout"; + public static final String CD_ACTIVITY_MOBILE_LOGIN = "mobile_login"; + public static final String CD_ACTIVITY_MOBILE_LOGOUT = "mobile_logout"; + public static final String CD_ACTIVITY_REMOVE_CHILD_ROLE = "remove_child_role"; + public static final String CD_ACTIVITY_REMOVE_ROLE = "remove_role"; + public static final String CD_ACTIVITY_REMOVE_ROLE_FUNCTION = "remove_role_function"; + public static final String CD_ACTIVITY_REMOVE_USER_ROLE = "remove_user_role"; + public static final String CD_ACTIVITY_TAB_ACCESS = "tab_access"; + public static final String CD_ACTIVITY_SEARCH = "search"; + public static final String CD_ACTIVITY_ADD_PORTAL_ADMIN = "apa "; + public static final String CD_ACTIVITY_DELETE_PORTAL_ADMIN = "dpa"; + public static final String CD_ACTIVITY_UPDATE_USER = "uu "; + public static final String CD_ACTIVITY_UPDATE_ACCOUNT_ADMIN = "uaa "; + public static final String CD_ACTIVITY_STORE_ANALYTICS = "store_analytics"; + + /** + * Creates a new object with the created field set to the current date-time. + */ + public EcompAuditLog() { + setCreated(new Date()); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EpUserAppRoles.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EpUserAppRoles.java new file mode 100644 index 00000000..1bfb1841 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/EpUserAppRoles.java @@ -0,0 +1,63 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@Entity +@JsonInclude(JsonInclude.Include.NON_NULL) +public class EpUserAppRoles extends DomainVo { + + private static final long serialVersionUID = -1484592641766545668L; + + @Id + @Column(name ="role_id") + public Long roleId; + + @Id + @Column(name ="app_id") + public Long appId; + + public Long getRoleId() { + return roleId; + } + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + public Long getAppId() { + return appId; + } + public void setAppId(Long appId) { + this.appId = appId; + } + + @Override + public String toString() { + return "EpUserAppRoles [roleId=" + roleId + ", appId=" + appId + "]"; + } +} + diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/ExternalSystemAccess.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/ExternalSystemAccess.java new file mode 100644 index 00000000..a0395bf8 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/ExternalSystemAccess.java @@ -0,0 +1,49 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +public class ExternalSystemAccess { + + private String key; + private Boolean accessValue; + + public ExternalSystemAccess(String key, Boolean accessValue){ + this.key = key; + this.accessValue = accessValue; + } + + public String getKey() { + return key; + } + public void setKey(String key) { + this.key = key; + } + + public Boolean getAccessValue() { + return accessValue; + } + + public void setAccessValue(Boolean accessValue) { + this.accessValue = accessValue; + } + + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/FunctionalMenuItemWithAppID.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/FunctionalMenuItemWithAppID.java new file mode 100644 index 00000000..ac2bc6c4 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/FunctionalMenuItemWithAppID.java @@ -0,0 +1,96 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Transient; +/*** + * + * This class is almost identical to org.openecomp.portalapp.portal.transport.FunctionalMenuItem + * The only difference is the appId field. In FunctionMenuItem, we used @Transient as an attribute, and we are using @Column in this class. + * In some sql queries, we need to have appId, but it's not a field in fn_menu_functional table. + * + * @author robertlo + * + */ +@Entity +public class FunctionalMenuItemWithAppID{ + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name = "MENU_ID") + public Long menuId; + + @Column(name = "COLUMN_NUM") + public Integer column; + + @Column(name = "TEXT") + public String text; + + @Column(name = "PARENT_MENU_ID") + public Integer parentMenuId; + + @Column(name = "URL") + public String url; + + @Column(name="ACTIVE_YN") + public String active_yn; + + @Column(name="APP_ID") + public Integer appid; + + @Transient + public List roles; + + @Transient + public Boolean restrictedApp; + + public void normalize() { + if (this.column == null) + this.column = new Integer(1); + this.text = (this.text == null) ? "" : this.text.trim(); + if (this.parentMenuId == null) + this.parentMenuId = new Integer(-1); + this.url = (this.url == null) ? "" : this.url.trim(); + } + + @Override + public String toString() { + return "FunctionalMenuItem [menuId=" + menuId + ", column=" + column + ", text=" + text + ", parentMenuId=" + + parentMenuId + ", url=" + url + ", active_yn=" + active_yn + ", appid=" + appid + ", roles=" + roles + + ", restrictedApp=" + restrictedApp + "]"; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setRestrictedApp(Boolean restrictedApp) { + this.restrictedApp = restrictedApp; + } +} + diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/GetAccessResult.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/GetAccessResult.java new file mode 100644 index 00000000..4669426a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/GetAccessResult.java @@ -0,0 +1,114 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@Entity +@JsonInclude +public class GetAccessResult implements Serializable{ + + + private static final long serialVersionUID = 5239527705869613411L; + + @Id + @Column(name="row_id") + private String rowId; + + @Column(name="role_id") + private Long roleId; + + @Column(name="ecomp_function",nullable=true) + private String ecompFunction; + + @Column(name="app_name") + private String appName; + + @Column(name="app_mots_id",nullable=true) + private Integer appMotsId; + + @Column(name="role_name") + private String roleName; + + @Column(name="role_actv",nullable=true) + private String roleActive; + + + @Column(name="request_type",nullable=true) + private String reqType; + + + + public final String getRowId() { + return rowId; + } + public final void setRowId(String rowId) { + this.rowId = rowId; + } + public final Long getRoleId() { + return roleId; + } + public final void setRoleId(Long roleId) { + this.roleId = roleId; + } + public String getEcompFunction() { + return ecompFunction; + } + public void setEcompFunction(String ecompFunction) { + this.ecompFunction = ecompFunction; + } + public String getAppName() { + return appName; + } + public void setAppName(String appName) { + this.appName = appName; + } + public Integer getAppMotsId() { + return appMotsId; + } + public void setAppMotsId(Integer appMotsId) { + this.appMotsId = appMotsId; + } + public String getRoleName() { + return roleName; + } + public void setRoleName(String roleName) { + this.roleName = roleName; + } + public final String getRoleActive() { + return roleActive; + } + public final void setRoleActive(String roleActive) { + this.roleActive = roleActive; + } + public final String getReqType() { + return reqType; + } + public final void setReqType(String reqType) { + this.reqType = reqType; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/GetAccessResultId.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/GetAccessResultId.java new file mode 100644 index 00000000..0830bfd8 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/GetAccessResultId.java @@ -0,0 +1,64 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Id; + +public class GetAccessResultId implements Serializable{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + @Id + @Column(name="ecomp_function") + private String ecompFunction; + + @Id + @Column(name="app_name") + private String appName; + + @Id + @Column(name="role_name") + private String roleName; + + public String getEcompFunction() { + return ecompFunction; + } + public void setEcompFunction(String ecompFunction) { + this.ecompFunction = ecompFunction; + } + public String getAppName() { + return appName; + } + public void setAppName(String appName) { + this.appName = appName; + } + public String getRoleName() { + return roleName; + } + public void setRoleName(String roleName) { + this.roleName = roleName; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/MicroserviceData.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/MicroserviceData.java new file mode 100644 index 00000000..91366ce8 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/MicroserviceData.java @@ -0,0 +1,146 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class MicroserviceData extends DomainVo { + + private static final long serialVersionUID = 1L; + + public MicroserviceData() { + + } + + private Long id; + + private String name; + + private String active; + + private String desc; + + private long appId; + + private String url; + + private String securityType; + + private String username; + + private String password; + + private List parameterList; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getActive() { + return active; + } + + public void setActive(String active) { + this.active = active; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public long getAppId() { + return appId; + } + + public void setAppId(long appId) { + this.appId = appId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getSecurityType() { + return securityType; + } + + public void setSecurityType(String securityType) { + this.securityType = securityType; + } + + 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 List getParameterList() { + return parameterList; + } + + public void setParameterList(List parameterList) { + this.parameterList = parameterList; + } + + @Override + public String toString() { + return "MicroserviceData [id=" + id + ", name=" + name + ", desc=" + desc + ", appId=" + appId + ", url=" + url + + ", securityType=" + securityType + ", username=" + username + ", password=" + password + + ", parameterList=" + parameterList + "]"; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/MicroserviceParameter.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/MicroserviceParameter.java new file mode 100644 index 00000000..74a29370 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/MicroserviceParameter.java @@ -0,0 +1,82 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class MicroserviceParameter extends DomainVo { + + private static final long serialVersionUID = 1L; + + public MicroserviceParameter() { + + } + + private Long id; + + private long serviceId; + + private String para_key; + + private String para_value; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public long getServiceId() { + return serviceId; + } + + public void setServiceId(long serviceId) { + this.serviceId = serviceId; + } + + public String getPara_key() { + return para_key; + } + + public void setPara_key(String para_key) { + this.para_key = para_key; + } + + public String getPara_value() { + return para_value; + } + + public void setPara_value(String para_value) { + this.para_value = para_value; + } + + @Override + public String toString() { + return "MicroserviceParameter [id=" + id + ", serviceId=" + serviceId + ", para_key=" + para_key + + ", para_value=" + para_value + "]"; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/PersUserAppSelection.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/PersUserAppSelection.java new file mode 100644 index 00000000..68875bf5 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/PersUserAppSelection.java @@ -0,0 +1,78 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +/** + * Models a row in the table with personalization of user app selections. + */ +public class PersUserAppSelection extends DomainVo { + + private static final long serialVersionUID = 1545308654500121206L; + + private Long userId; + + private Long appId; + + private String statusCode; + + public PersUserAppSelection() {} + + /** + * Convenience constructor + * + * @param id + * @param userId + * @param appId + * @param statusCode + */ + public PersUserAppSelection(final Long id, final Long userId, final Long appId, final String statusCode) { + super.id = id; + this.userId = userId; + this.appId = appId; + this.statusCode = statusCode; + } + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getStatusCode() { + return statusCode; + } + + public void setStatusCode(String statusCode) { + this.statusCode = statusCode; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/PersUserWidgetSelection.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/PersUserWidgetSelection.java new file mode 100644 index 00000000..4993b462 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/PersUserWidgetSelection.java @@ -0,0 +1,81 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +/** + * Models a row in the table with personalization of user widget selections. + */ +public class PersUserWidgetSelection extends DomainVo { + + /** + * + */ + private static final long serialVersionUID = -6547880514779039200L; + + private Long userId; + + private Long widgetId; + + private String statusCode; + + public PersUserWidgetSelection() {} + + /** + * Convenience constructor + * + * @param id + * @param userId + * @param widgetId + * @param statusCode + */ + public PersUserWidgetSelection(final Long id, final Long userId, final Long widgetId, final String statusCode) { + super.id = id; + this.userId = userId; + this.widgetId = widgetId; + this.statusCode = statusCode; + } + + public Long getWidgetId() { + return widgetId; + } + + public void setWidgetId(Long widgetId) { + this.widgetId = widgetId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getStatusCode() { + return statusCode; + } + + public void setStatusCode(String statusCode) { + this.statusCode = statusCode; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/RoleApp.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/RoleApp.java new file mode 100644 index 00000000..ade24dcb --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/RoleApp.java @@ -0,0 +1,114 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +//@Entity +//@Table(name = "FN_ROLE") +public class RoleApp implements Serializable{ + private static final long serialVersionUID = 1L; + + //@Id + //@Column(name = "ROLE_ID") + //@GeneratedValue(strategy=GenerationType.AUTO) + private Long roleId; + + + //@Column(name = "ROLE_Name") + private String roleName; + + //@ManyToOne(fetch = FetchType.EAGER) + //@JoinColumn(name="APP_ID") + private App app; + + //@JsonIgnore + //@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, mappedBy="widgetRoles") + private Set widgets; + + /*@PreRemove + private void removeGroupsFromUsers() { + for (WidgetCatalog w : widgets) { + w.getWidgetRoles().remove(this); + } + }*/ + + /*@ManyToOne + @JoinColumn(name = "WIDGET_ID", nullable = false) + WidgetCatalog widgetCatalog;*/ + + //@JsonIgnore + //@ManyToMany(mappedBy = "widgetRoles") + //@ManyToMany(fetch = FetchType.EAGER, mappedBy = "widgetRoles") + //private Set widgets = new HashSet(); + + public Long getRoleId() { + return roleId; + } + + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public App getApp() { + return app; + } + + public void setApp(App app) { + this.app = app; + } + + + + public Set getWidgets() { + return widgets; + } + + public void setWidgets(Set widgets) { + this.widgets = widgets; + } + + @Override + public String toString() { + return "Role [roleId=" + roleId + ", roleName=" + roleName + ", app=" + app + "]"; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/SharedContext.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/SharedContext.java new file mode 100644 index 00000000..f966c964 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/SharedContext.java @@ -0,0 +1,161 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +/** + * A shared context is a key-value pair in a session. All shared-context objects + * should be dropped when a session is destroyed. Because there's always a + * chance of missing that event, this object notes its creation time so that it + * can be expired after a suitable time interval. + */ +@Entity +@Table(name = "fn_shared_context") +public class SharedContext extends DomainVo { + + // generated + private static final long serialVersionUID = 7287469622586677888L; + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + private Date create_time; + private String context_id; + private String ckey; + private String cvalue; + + /** + * Mandatory no-argument constructor + */ + public SharedContext() { + } + + /** + * Convenience constructor. The database ID and creation timestamp are + * populated when the object is added to the database. + */ + public SharedContext(final String contextId, final String key, final String value) { + this.context_id = contextId; + this.ckey = key; + this.cvalue = value; + } + + /** + * Gets the database row ID. + * + * @return Database row ID + */ + public Long getId() { + return id; + } + + /** + * Sets the database row ID. + * + * @param id + */ + public void setId(final Long id) { + this.id = id; + } + + /** + * Gets the creation time + * + * @return Creation time as a Date + */ + public Date getCreate_time() { + return create_time; + } + + /** + * Sets the creation time + * + * @param create_time + */ + public void setCreate_time(final Date create_time) { + this.create_time = create_time; + } + + /** + * Gets the context ID + * + * @return Context ID + */ + public String getContext_id() { + return context_id; + } + + /** + * Sets the context ID + * + * @param context_id + */ + public void setContext_id(final String context_id) { + this.context_id = context_id; + } + + /** + * Gets the key of the key-value pair. Called ckey because "key" is a + * reserved word in Mysql. + * + * @return The key + */ + public String getCkey() { + return ckey; + } + + /** + * Sets the key of the key-value pair. + * + * @param ckey + */ + public void setCkey(final String ckey) { + this.ckey = ckey; + } + + /** + * Gets the value of the key-value pair. Called cvalue because "value" is a + * reserved word in Mysql. + * + * @return + */ + public String getCvalue() { + return cvalue; + } + + /** + * Sets the value of the key-value pair. + * + * @param cvalue + */ + public void setCvalue(final String cvalue) { + this.cvalue = cvalue; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserIdRoleId.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserIdRoleId.java new file mode 100644 index 00000000..4d2041dd --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserIdRoleId.java @@ -0,0 +1,77 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class UserIdRoleId implements Serializable{ + + private static final long serialVersionUID = 1L; + + @Id + @Column(name="USER_ID") + private String user_Id; + + @Column(name="ROLE_ID") + private String roleId; + + @Column(name="ORG_USER_ID") + private String orgUserId; + + @Column(name="APP_ID") + private String appId; + + public String getUser_Id() { + return user_Id; + } + + public void setUser_Id(String id) { + this.user_Id = id; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getOrgUserId() { + return orgUserId; + } + + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserRole.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserRole.java new file mode 100644 index 00000000..0e13cc01 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserRole.java @@ -0,0 +1,91 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class UserRole implements Serializable{ + private static final long serialVersionUID = 1L; + + + @Id + @Column(name="USER_ID") + private Long user_Id; + + @Id + @Column(name="ROLE_ID") + private Long roleId; + + @Column(name="ORG_USER_ID") + private String orgUserId; + + + @Column(name="FIRST_NAME") + private String firstName; + + @Column(name="LAST_NAME") + private String lastName; + + + @Column(name="ROLE_NAME") + private String roleName; + + 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 getOrgUserId() { + return orgUserId; + } + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } + public String getRoleName() { + return roleName; + } + public void setRoleName(String roleName) { + this.roleName = roleName; + } + public Long getUser_Id() { + return user_Id; + } + public void setUser_Id(Long user_Id) { + this.user_Id = user_Id; + } + public Long getRoleId() { + return roleId; + } + public void setRoleId(Long roleId) { + this.roleId = roleId; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserRoles.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserRoles.java new file mode 100644 index 00000000..1483f34a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/UserRoles.java @@ -0,0 +1,113 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; + +public class UserRoles implements Serializable { + private static final long serialVersionUID = 1L; + + /* + private static final HashMap rolesDictionary; + // remove hard coded roles + static { + rolesDictionary = new HashMap(); + rolesDictionary.put(Long.valueOf(SystemProperties.getProperty(SystemProperties.SYS_ADMIN_ROLE_ID)).longValue(), "superAdmin"); + rolesDictionary.put(Long.valueOf(SystemProperties.getProperty(EPCommonSystemProperties.ACCOUNT_ADMIN_ROLE_ID)).longValue(), "admin"); + } + */ + + public UserRoles(UserRole user) { + setOrgUserId(user.getOrgUserId()); + setFirstName(user.getFirstName()); + setLastName(user.getLastName()); + setGuestSession(user.getUser_Id()==-1 ? true : false); + + addRole(user.getRoleName()); + } + + public void addRole(String roleName) { + //String normalizedRole = normalizeRole(roleId); + if (!getRoles().contains(roleName)) { + this.roles.add(roleName); + } + } + + /* + public static String normalizeRole(Long role) { + String roleTranslated = rolesDictionary.get(role); + return roleTranslated == null ? "user" : roleTranslated; + } + */ + + private String orgUserId; + private String firstName; + private String lastName; + private boolean guestSession; + + // TODO: Make into set + private List roles = new ArrayList(); + + 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 getOrgUserId() { + return orgUserId; + } + + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + public void setGuestSession(boolean guestSession) { + this.guestSession = guestSession; + } + + public boolean getGuestSession() { + return this.guestSession; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/Widget.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/Widget.java new file mode 100644 index 00000000..c2ef3c00 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/Widget.java @@ -0,0 +1,105 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import org.apache.commons.lang.StringUtils; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class Widget extends DomainVo { + + private static final long serialVersionUID = 1L; + + private String name; + + private Integer width; + + private Integer height; + + private String url; + + private Long appId; + + public Widget() { + // Attention!!! + // We set here all default values. We also place protection + // into setters for fields with default values. + // If we don't use such protection we are able to place null + // to these fields and save such fields into DB even if DB has + // default values for these fields. + this.name = ""; + this.width = new Integer(0); + this.height = new Integer(0); + this.url = ""; + } + + public String getName() { + return name; + } + + public void setName(String name) { + if (StringUtils.isEmpty(name)) { + name = ""; + } + this.name = name; + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + if (width == null) { + width = new Integer(0); + } + this.width = width; + } + + public Integer getHeight() { + return height; + } + + public void setHeight(Integer height) { + if (height == null) { + height = new Integer(0); + } + this.height = height; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + if (StringUtils.isEmpty(url)) { + url = ""; + } + this.url = url; + } + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetCatalog.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetCatalog.java new file mode 100644 index 00000000..605a41d2 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetCatalog.java @@ -0,0 +1,109 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.io.Serializable; +import java.util.Set; + +public class WidgetCatalog implements Serializable{ + + private static final long serialVersionUID = 1L; + + private long id; + + private String name; + + private String desc; + + private String fileLocation; + + private String allowAllUser; + + private Long serviceId; + + private String sortOrder; + + private String statusCode; + + private Set widgetRoles; + + public long getId() { + return id; + } + public void setId(long id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public Long getServiceId() { + return serviceId; + } + public void setServiceId(Long serviceId) { + this.serviceId = serviceId; + } + public String getDesc() { + return desc; + } + public void setDesc(String desc) { + this.desc = desc; + } + public String getFileLocation() { + return fileLocation; + } + public void setFileLocation(String fileLocation) { + this.fileLocation = fileLocation; + } + + public String getAllowAllUser() { + return allowAllUser; + } + public void setAllowAllUser(String allowAllUser) { + this.allowAllUser = allowAllUser; + } + public Set getWidgetRoles() { + return widgetRoles; + } + public void setWidgetRoles(Set widgetRoles) { + this.widgetRoles = widgetRoles; + } + + public String getSortOrder() { + return sortOrder; + } + public void setSortOrder(String sortOrder) { + this.sortOrder = sortOrder; + } + public String getStatusCode() { + return statusCode; + } + public void setStatusCode(String statusCode) { + this.statusCode = statusCode; + } + @Override + public String toString() { + return "WidgetCatalog [id=" + id + ", name=" + name + ", desc=" + desc + ", fileLocation=" + fileLocation + + ", allowAllUser=" + allowAllUser + "]"; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetCatalogParameter.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetCatalogParameter.java new file mode 100644 index 00000000..c72780aa --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetCatalogParameter.java @@ -0,0 +1,90 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +public class WidgetCatalogParameter extends DomainVo{ + + private static final long serialVersionUID = 1L; + + public WidgetCatalogParameter() { + + } + private Long id; + + private Long widgetId; + + private Long userId; + + private Long paramId; + + private String user_value; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getWidgetId() { + return widgetId; + } + + public void setWidgetId(Long widgetId) { + this.widgetId = widgetId; + } + + public String getUser_value() { + return user_value; + } + + public void setUser_value(String user_value) { + this.user_value = user_value; + } + + public Long getParamId() { + return paramId; + } + + public void setParamId(Long paramId) { + this.paramId = paramId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + @Override + public String toString() { + return "WidgetCatalogParameter [id=" + id + ", widgetId=" + widgetId + ", userId=" + userId + ", paramId=" + + paramId + ", user_value=" + user_value + "]"; + } + + + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetParameterResult.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetParameterResult.java new file mode 100644 index 00000000..884ec141 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetParameterResult.java @@ -0,0 +1,70 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +public class WidgetParameterResult { + + private Long param_id; + + private String param_key; + + private String user_value; + + private String default_value; + + public Long getParam_id() { + return param_id; + } + + public void setParam_id(Long param_id) { + this.param_id = param_id; + } + + public String getParam_key() { + return param_key; + } + + public void setParam_key(String param_key) { + this.param_key = param_key; + } + + public String getUser_value() { + return user_value; + } + + public void setUser_value(String user_value) { + this.user_value = user_value; + } + + public String getDefault_value() { + return default_value; + } + + public void setDefault_value(String default_value) { + this.default_value = default_value; + } + + @Override + public String toString() { + return "WidgetParameterResult [param_id=" + param_id + ", param_key=" + param_key + ", user_value=" + user_value + + ", default_value=" + default_value + "]"; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetServiceHeaders.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetServiceHeaders.java new file mode 100644 index 00000000..9200adda --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/domain/WidgetServiceHeaders.java @@ -0,0 +1,54 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.domain; + +import java.nio.charset.Charset; + +import org.apache.commons.codec.binary.Base64; +import org.springframework.http.HttpHeaders; + +import org.openecomp.portalsdk.core.onboarding.util.CipherUtil; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; + +public class WidgetServiceHeaders { + + private static HttpHeaders widgetHeaders; + + private WidgetServiceHeaders(){} + + public static HttpHeaders getInstance() throws Exception{ + if(null == widgetHeaders){ + return new HttpHeaders(){{ + + String username = EcompPortalUtils.getPropertyOrDefault("microservices.widget.username", "widget_user"); + String password = CipherUtil.decrypt(EcompPortalUtils.getPropertyOrDefault("microservices.widget.password", "widget_password")); + String auth = username + ":" + password; + byte[] encodedAuth = Base64.encodeBase64( + auth.getBytes(Charset.forName("US-ASCII")) ); + String authHeader = "Basic " + new String( encodedAuth ); + set( "Authorization", authHeader ); + } + }; + } + else + return widgetHeaders; + + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppCatalogItem.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppCatalogItem.java new file mode 100644 index 00000000..ec20b4f3 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppCatalogItem.java @@ -0,0 +1,249 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.ecomp.model; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * This model of an application catalog entry has some EPApp fields plus + * additional fields to indicate access(ible) and select(ed) statuses. + */ +@Entity +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AppCatalogItem extends DomainVo { + + private static final long serialVersionUID = 6619663784935017846L; + + @Id + private Long id; + private String name; + private String imageUrl; + private String description; + private String notes; + private String url; + private String alternateUrl; + private Boolean restricted; + private Boolean open; + private Boolean access; + private Boolean select; + private Boolean pending; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getAlternateUrl() { + return alternateUrl; + } + + public void setAlternateUrl(String alternateUrl) { + this.alternateUrl = alternateUrl; + } + + public Boolean getRestricted() { + return restricted; + } + + public void setRestricted(Boolean restricted) { + this.restricted = restricted; + } + + public Boolean getOpen() { + return open; + } + + public void setOpen(Boolean open) { + this.open = open; + } + + public Boolean getAccess() { + return access; + } + + public void setAccess(Boolean access) { + this.access = access; + } + + public Boolean getSelect() { + return select; + } + + public void setSelect(Boolean select) { + this.select = select; + } + + public Boolean getPending() { + return pending; + } + + public void setPending(Boolean pending) { + this.pending = pending; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + @Override + public String toString() { + return "AppCatalogItem [id=" + id + ", name=" + name + ", access=" + access + ", select=" + select + + ", pending=" + pending + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((access == null) ? 0 : access.hashCode()); + result = prime * result + ((alternateUrl == null) ? 0 : alternateUrl.hashCode()); + result = prime * result + ((description == null) ? 0 : description.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((imageUrl == null) ? 0 : imageUrl.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((notes == null) ? 0 : notes.hashCode()); + result = prime * result + ((open == null) ? 0 : open.hashCode()); + result = prime * result + ((pending == null) ? 0 : pending.hashCode()); + result = prime * result + ((restricted == null) ? 0 : restricted.hashCode()); + result = prime * result + ((select == null) ? 0 : select.hashCode()); + result = prime * result + ((url == null) ? 0 : url.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AppCatalogItem other = (AppCatalogItem) obj; + if (access == null) { + if (other.access != null) + return false; + } else if (!access.equals(other.access)) + return false; + if (alternateUrl == null) { + if (other.alternateUrl != null) + return false; + } else if (!alternateUrl.equals(other.alternateUrl)) + return false; + if (description == null) { + if (other.description != null) + return false; + } else if (!description.equals(other.description)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (imageUrl == null) { + if (other.imageUrl != null) + return false; + } else if (!imageUrl.equals(other.imageUrl)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (notes == null) { + if (other.notes != null) + return false; + } else if (!notes.equals(other.notes)) + return false; + if (open == null) { + if (other.open != null) + return false; + } else if (!open.equals(other.open)) + return false; + if (pending == null) { + if (other.pending != null) + return false; + } else if (!pending.equals(other.pending)) + return false; + if (restricted == null) { + if (other.restricted != null) + return false; + } else if (!restricted.equals(other.restricted)) + return false; + if (select == null) { + if (other.select != null) + return false; + } else if (!select.equals(other.select)) + return false; + if (url == null) { + if (other.url != null) + return false; + } else if (!url.equals(other.url)) + return false; + return true; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppCategoryFunctionsItem.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppCategoryFunctionsItem.java new file mode 100644 index 00000000..b1601431 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppCategoryFunctionsItem.java @@ -0,0 +1,140 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.ecomp.model; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Carries row information for the functional table on the Contact Us page. + */ +@Entity +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AppCategoryFunctionsItem extends DomainVo { + + private static final long serialVersionUID = -1573834082471206458L; + + @Id + private String rowId; + private String appId; + private String application; + private String category; + private String functions; + + public String getRowId() { + return rowId; + } + + public void setRowId(String rowId) { + this.rowId = rowId; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getApplication() { + return application; + } + + public void setApplication(String appName) { + this.application = appName; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getFunctions() { + return functions; + } + + public void setFunctions(String functions) { + this.functions = functions; + } + + @Override + public String toString() { + return "AppCategoryFunctionsItem [rowId=" + rowId + ", appId=" + appId + ", application=" + application + + ", category=" + category + ", functions=" + functions + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((appId == null) ? 0 : appId.hashCode()); + result = prime * result + ((application == null) ? 0 : application.hashCode()); + result = prime * result + ((category == null) ? 0 : category.hashCode()); + result = prime * result + ((functions == null) ? 0 : functions.hashCode()); + result = prime * result + ((rowId == null) ? 0 : rowId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AppCategoryFunctionsItem other = (AppCategoryFunctionsItem) obj; + if (appId == null) { + if (other.appId != null) + return false; + } else if (!appId.equals(other.appId)) + return false; + if (application == null) { + if (other.application != null) + return false; + } else if (!application.equals(other.application)) + return false; + if (category == null) { + if (other.category != null) + return false; + } else if (!category.equals(other.category)) + return false; + if (functions == null) { + if (other.functions != null) + return false; + } else if (!functions.equals(other.functions)) + return false; + if (rowId == null) { + if (other.rowId != null) + return false; + } else if (!rowId.equals(other.rowId)) + return false; + return true; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppContactUsItem.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppContactUsItem.java new file mode 100644 index 00000000..5dc7bf57 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/AppContactUsItem.java @@ -0,0 +1,171 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.ecomp.model; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * An easily serializable version of row information from the fn_app_contact_us + * table; specifically this has an app ID instead of an EPApp object. + */ +@Entity +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AppContactUsItem extends DomainVo { + + private static final long serialVersionUID = 6964210807573346262L; + + @Id + private Long appId; + private String appName; + private String description; + private String contactName; + private String contactEmail; + private String url; + private String activeYN; + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getContactName() { + return contactName; + } + + public void setContactName(String contactName) { + this.contactName = contactName; + } + + public String getContactEmail() { + return contactEmail; + } + + public void setContactEmail(String contactEmail) { + this.contactEmail = contactEmail; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getActiveYN() { + return activeYN; + } + + public void setActiveYN(String activeYN) { + this.activeYN = activeYN; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((activeYN == null) ? 0 : activeYN.hashCode()); + result = prime * result + ((appId == null) ? 0 : appId.hashCode()); + result = prime * result + ((appName == null) ? 0 : appName.hashCode()); + result = prime * result + ((contactEmail == null) ? 0 : contactEmail.hashCode()); + result = prime * result + ((contactName == null) ? 0 : contactName.hashCode()); + result = prime * result + ((description == null) ? 0 : description.hashCode()); + result = prime * result + ((url == null) ? 0 : url.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AppContactUsItem other = (AppContactUsItem) obj; + if (activeYN == null) { + if (other.activeYN != null) + return false; + } else if (!activeYN.equals(other.activeYN)) + return false; + if (appId == null) { + if (other.appId != null) + return false; + } else if (!appId.equals(other.appId)) + return false; + if (appName == null) { + if (other.appName != null) + return false; + } else if (!appName.equals(other.appName)) + return false; + if (contactEmail == null) { + if (other.contactEmail != null) + return false; + } else if (!contactEmail.equals(other.contactEmail)) + return false; + if (contactName == null) { + if (other.contactName != null) + return false; + } else if (!contactName.equals(other.contactName)) + return false; + if (description == null) { + if (other.description != null) + return false; + } else if (!description.equals(other.description)) + return false; + if (url == null) { + if (other.url != null) + return false; + } else if (!url.equals(other.url)) + return false; + return true; + } + + @Override + public String toString() { + return "AppContactUsItem [appId=" + appId + ", appName=" + appName + ", description=" + description + + ", contactName=" + contactName + ", contactEmail=" + contactEmail + ", url=" + url + ", activeYN=" + + activeYN + "]"; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/CommonWidgetsEnum.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/CommonWidgetsEnum.java new file mode 100644 index 00000000..994108dd --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/CommonWidgetsEnum.java @@ -0,0 +1,36 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.ecomp.model; + +public enum CommonWidgetsEnum{ + NEWS("news"), + EVENTS("events"), + RESOURCES("resources"); + + private String value; + private CommonWidgetsEnum(String value){ + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/PortalRestResponse.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/PortalRestResponse.java new file mode 100644 index 00000000..3572d6a6 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/PortalRestResponse.java @@ -0,0 +1,101 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.ecomp.model; + +public class PortalRestResponse { + + private PortalRestStatusEnum status; + private String message; + + private T response; + + public PortalRestResponse(){}; + + public PortalRestResponse(PortalRestStatusEnum status, String message, T response){ + this.status = status; + this.message = message; + this.response = response; + } + + public PortalRestStatusEnum getStatus() { + return status; + } + + public void setStatus(PortalRestStatusEnum status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public T getResponse() { + return response; + } + + public void setResponse(T response) { + this.response = response; + } + + @Override + public String toString() { + return "PortalRestResponse [status=" + status + ", message=" + message + ", response=" + response + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((message == null) ? 0 : message.hashCode()); + result = prime * result + ((response == null) ? 0 : response.hashCode()); + result = prime * result + ((status == null) ? 0 : status.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PortalRestResponse other = (PortalRestResponse) obj; + if (message == null) { + if (other.message != null) + return false; + } else if (!message.equals(other.message)) + return false; + if (response == null) { + if (other.response != null) + return false; + } else if (!response.equals(other.response)) + return false; + if (status != other.status) + return false; + return true; + }; + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/PortalRestStatusEnum.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/PortalRestStatusEnum.java new file mode 100644 index 00000000..b4dc63ab --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/PortalRestStatusEnum.java @@ -0,0 +1,36 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.ecomp.model; + +public enum PortalRestStatusEnum{ + OK("ok"), + WARN("WARNING"), + ERROR("error"); + + private String value; + private PortalRestStatusEnum(String value){ + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/SearchResultItem.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/SearchResultItem.java new file mode 100644 index 00000000..c6538470 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ecomp/model/SearchResultItem.java @@ -0,0 +1,86 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.ecomp.model; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@Entity +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SearchResultItem extends DomainVo{ + + @Id + private String rowId; + + @JsonProperty("category") + private String category; + + @JsonProperty("name") + private String name; + + @JsonProperty("target") + private String target; + + @JsonProperty("uuid") + private String uuid; + + public String getUuid() { + return uuid; + } + public void setUuid(String uuid) { + this.uuid = uuid; + } + public String getRowId() { + return rowId; + } + public void setRowId(String rowId) { + this.rowId = rowId; + } + public String getCategory() { + return category; + } + public void setCategory(String category) { + this.category = category; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public String getTarget() { + return target; + } + public void setTarget(String target) { + this.target = target; + } + @Override + public String toString() { + return "SearchResultItem [rowId=" + rowId + ", category=" + category + ", name=" + name + ", target=" + target + + ", uuid=" + uuid + "]"; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/exceptions/NoHealthyServiceException.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/exceptions/NoHealthyServiceException.java new file mode 100644 index 00000000..ce0366c8 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/exceptions/NoHealthyServiceException.java @@ -0,0 +1,36 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.exceptions; + +import com.ecwid.consul.ConsulException; + +public class NoHealthyServiceException extends ConsulException{ + + private static final long serialVersionUID = -7089437673127729813L; + + public NoHealthyServiceException(String message){ + super(message); + } + + @Override + public String toString() { + return "NoHealthyServiceException [] " + this.getMessage(); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/interceptor/PortalResourceInterceptor.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/interceptor/PortalResourceInterceptor.java new file mode 100644 index 00000000..9a2fc4de --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/interceptor/PortalResourceInterceptor.java @@ -0,0 +1,335 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================*/ +package org.openecomp.portalapp.portal.interceptor; + +import java.net.URL; +import java.nio.charset.Charset; +import java.util.Base64; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalapp.controller.sessionmgt.SessionCommunicationController; +import org.openecomp.portalapp.portal.controller.BasicAuthenticationController; +import org.openecomp.portalapp.portal.controller.ExternalAppsRestfulController; +import org.openecomp.portalapp.portal.controller.SharedContextRestController; +import org.openecomp.portalapp.portal.controller.WebAnalyticsExtAppController; +import org.openecomp.portalapp.portal.domain.BasicAuthCredentials; +import org.openecomp.portalapp.portal.domain.EPEndpoint; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.aop.EPEELFLoggerAdvice; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.service.BasicAuthenticationCredentialService; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.service.RemoteWebServiceCallService; +import org.openecomp.portalapp.service.sessionmgt.ManageService; +import org.openecomp.portalapp.util.EPUserUtils; +import org.openecomp.portalsdk.core.controller.FusionBaseController; +import org.openecomp.portalsdk.core.exception.UrlAccessRestrictedException; +import org.openecomp.portalsdk.core.interceptor.ResourceInterceptor; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.listener.PortalTimeoutHandler; +import org.openecomp.portalsdk.core.onboarding.util.CipherUtil; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalsdk.core.util.SystemProperties.SecurityEventTypeEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.method.HandlerMethod; + +public class PortalResourceInterceptor extends ResourceInterceptor { + private static final String APP_KEY = "uebkey"; + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(PortalResourceInterceptor.class); + + @Autowired + private RemoteWebServiceCallService remoteWebServiceCallService; + + @Autowired + private ManageService manageService; + + @Autowired + private EPEELFLoggerAdvice epAdvice; + + @Autowired + private BasicAuthenticationCredentialService basicAuthService; + + @SuppressWarnings("unchecked") + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + + if (handler instanceof HandlerMethod) { + HandlerMethod method = (HandlerMethod) handler; + + /** + * These classes provide REST endpoints used by other application + * servers, NOT by an end user's browser. + */ + if (method.getBean() instanceof FusionBaseController) { + FusionBaseController controller = (FusionBaseController) method.getBean(); + if (!controller.isAccessible()) { + + // authorize portalApi requests by user role + String requestURI = request.getRequestURI(); + if (requestURI != null) { + String[] uriArray = requestURI.split("/portalApi/"); + if (uriArray.length > 1) { + String portalApiPath = uriArray[1]; + + Set roleFunctions = (Set) request.getSession() + .getAttribute(SystemProperties + .getProperty(SystemProperties.ROLE_FUNCTIONS_ATTRIBUTE_NAME)); + Set allRoleFunctions = (Set) request.getSession() + .getAttribute(EPUserUtils.ALL_ROLE_FUNCTIONS); + // Defend against code error to avoid throwing NPE + if (roleFunctions == null || allRoleFunctions == null) { + logger.error(EELFLoggerDelegate.errorLogger, + "preHandle: failed to get role functions attribute(s) from session!!"); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInitializationError); + return false; + } + // check to see if roleFunctions of the user is in + // the + // list of all role functions + // if not, ignore to prevent restricting every + // trivial + // call; otherwise, if it is, then check for the + // access + if (matchRoleFunctions(portalApiPath, allRoleFunctions) + && !matchRoleFunctions(portalApiPath, roleFunctions)) { + EPUser user = (EPUser) request.getSession().getAttribute( + SystemProperties.getProperty(SystemProperties.USER_ATTRIBUTE_NAME)); + logger.error(EELFLoggerDelegate.errorLogger, + "preHandle: User {} not authorized for path {} ", user.getOrgUserId(), + portalApiPath); + EcompPortalUtils.setBadPermissions(user, response, portalApiPath); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiAuthenticationError); + return false; + } // failed to match + + } // is portalApi + + } // requestURI + } // instance check + } // not accessible + else if (method.getBean() instanceof BasicAuthenticationController) { + return checkBasicAuth(request, response); + } + Object controllerObj = method.getBean(); + if (controllerObj instanceof SessionCommunicationController + || controllerObj instanceof SharedContextRestController + || controllerObj instanceof ExternalAppsRestfulController) { + // check user authentication for RESTful calls + String secretKey = null; + try { + epAdvice.loadServletRequestBasedDefaults(request, SecurityEventTypeEnum.INCOMING_REST_MESSAGE); + if (!remoteWebServiceCallService.verifyRESTCredential(secretKey, request.getHeader(APP_KEY), + request.getHeader("username"), request.getHeader("password"))) { + throw new UrlAccessRestrictedException(); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "preHandle: failed to authenticate RESTful service", + e); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiAuthenticationError, e); + throw new UrlAccessRestrictedException(); + } + } + + if (controllerObj instanceof WebAnalyticsExtAppController) { + if (!remoteWebServiceCallService.verifyAppKeyCredential(request.getHeader(APP_KEY))) { + logger.error(EELFLoggerDelegate.errorLogger, + "preHandle: failed to verify app key for web analytics call"); + throw new UrlAccessRestrictedException(); + } + } + } + + handleSessionUpdates(request); + return true; + } + + /** + * Sets the status code and sends a response. Factors code out of many + * methods. + * + * @param response + * HttpServletResponse + * @param statusCode + * HTTP status code like 404 + * @param message + * Message to send in a JSON error object + */ + private void sendErrorResponse(HttpServletResponse response, final int statusCode, final String message) + throws Exception { + response.setStatus(statusCode); + response.setContentType("application/json"); + response.getWriter().write("{\"error\":\"" + message + "\"}"); + response.getWriter().flush(); + } + + /** + * Gets HTTP basic authentication information from the request and checks + * whether those credentials are authorized for the request path. + * + * @param request + * HttpServletRequest + * @param response + * HttpServletResponse + * @return True if the request is authorized, else false + * @throws Exception + */ + private boolean checkBasicAuth(HttpServletRequest request, HttpServletResponse response) throws Exception { + + String uri = request.getRequestURI().toString(); + uri = uri.substring(uri.indexOf("/", 1)); + + final String authHeader = request.getHeader("Authorization"); + if (authHeader == null) { + final String msg = "no authorization found"; + logger.debug(EELFLoggerDelegate.debugLogger, "checkBasicAuth: {}", msg); + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, msg); + return false; + } + + String[] accountNamePassword = getUserNamePassword(authHeader); + if (accountNamePassword == null || accountNamePassword.length != 2) { + final String msg = "failed to get username and password from auth header"; + logger.debug(EELFLoggerDelegate.debugLogger, "checkBasicAuth: {}", msg); + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, msg); + return false; + } + + BasicAuthCredentials creds; + try { + creds = basicAuthService.getBasicAuthCredentialByAppName(accountNamePassword[0]); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "checkBasicAuth failed to get credentials", e); + final String msg = "Failed while getting basic authentication credential: " + e.toString(); + sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg); + throw e; + } + + boolean isAllowedEp = false; + for(EPEndpoint ep: creds.getEndpoints()){ + if(ep.getName().equals(uri)){ + isAllowedEp = true; + break; + } + } + if(!isAllowedEp){ + response.setStatus(401); + response.setContentType("application/json"); + response.getWriter().write("{\"error\":\"Unauthorized: Endpoint access denied\"}"); + response.getWriter().flush(); + response.getWriter().close(); + return false; + } + + if (creds == null) { + final String msg = "failed to find match for credentials"; + logger.debug(EELFLoggerDelegate.debugLogger, "checkBasicAuth: {}", msg); + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, msg); + return false; + } + + boolean isAuth; + try { + isAuth = authorization(authHeader, creds.getApplicationName(), creds.getPassword()); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "checkBasicAuth failed to check authorization", e); + final String msg = "failed while checking authorization: " + e.toString(); + sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg); + throw e; + } + + if (!isAuth) { + response.setStatus(401); + response.setContentType("application/json"); + response.getWriter().write("{\"error\":\"Unauthorized: Invalid username or password\"}"); + response.getWriter().flush(); + response.getWriter().close(); + final String msg = "Unauthorized: Access denied"; + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, msg); + return false; + } + + // Made it to the end! + return true; + } + + /** + * + * Basic Authorization check + * + * @param auth + * @param security_user + * @param security_pass + * @return + * @throws Exception + */ + + protected boolean authorization(String auth, String security_user, String security_pass) throws Exception { + + if (auth != null && auth.startsWith("Basic")) { + String[] usernamePassword = getUserNamePassword(auth); + if (security_user.equals(usernamePassword[0]) && decrypted(security_pass).equals(usernamePassword[1])) + return true; + } + return false; + } + + public static void main(String str[]) { + System.out.println(new PortalResourceInterceptor().getUserNamePassword("Basic Qy1CVVM6X3Bhc3M=")[0]); + System.out.println(new PortalResourceInterceptor().getUserNamePassword("Basic Qy1CVVM6X3Bhc3M=")[1]); + } + + private String[] getUserNamePassword(String authValue) { + String base64Credentials = authValue.substring("Basic".length()).trim(); + String credentials = new String(Base64.getDecoder().decode(base64Credentials), Charset.forName("UTF-8")); + final String[] values = credentials.split(":", 2); + return values; + } + + private String decrypted(String encrypted) throws Exception { + String result = ""; + if (encrypted != null & encrypted.length() > 0) { + try { + result = CipherUtil.decrypt(encrypted, SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "decryptedPassword failed", e); + throw e; + } + } + return result; + } + + private Boolean matchRoleFunctions(String portalApiPath, Set roleFunctions) { + for (String roleFunction : roleFunctions) { + if (portalApiPath.matches(roleFunction)) + return true; + } + return false; + + } + + protected void handleSessionUpdates(HttpServletRequest request) { + PortalTimeoutHandler.handleSessionUpdatesNative(request, null, null, null, null, manageService); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/HealthMonitor.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/HealthMonitor.java new file mode 100644 index 00000000..b8e608cd --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/HealthMonitor.java @@ -0,0 +1,355 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.listener; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.ueb.EPUebHelper; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class HealthMonitor { + + @Autowired + private SessionFactory sessionFactory; + + @Autowired + private EPUebHelper epUebHelper; + + private static boolean databaseUp; + private static boolean uebUp; + private static boolean frontEndUp; + private static boolean backEndUp; + private static boolean dbClusterStatusOk; + private static boolean dbPermissionsOk; + public static boolean isSuspended = false; + + Thread healthMonitorThread; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(HealthMonitor.class); + + public HealthMonitor() { + + } + + public static boolean isDatabaseUp() { + return databaseUp; + } + + public static boolean isClusterStatusOk() { + return dbClusterStatusOk; + } + + public static boolean isDatabasePermissionsOk() { + return dbPermissionsOk; + } + + public static boolean isUebUp() { + return uebUp; + } + + public static boolean isFrontEndUp() { + return frontEndUp; + } + + public static boolean isBackEndUp() { + return backEndUp; + } + + private void monitorEPHealth() throws InterruptedException { + + int numIntervalsDatabaseHasBeenDown = 0; + int numIntervalsClusterNotHealthy = 0; + int numIntervalsDatabasePermissionsIncorrect = 0; + int numIntervalsUebHasBeenDown = 0; + + logger.debug(EELFLoggerDelegate.debugLogger, "==> Health Monitor thread started"); + + long sleepInterval = (Long + .valueOf(SystemProperties.getProperty(EPCommonSystemProperties.HEALTH_POLL_INTERVAL_SECONDS)) * 1000); + long numIntervalsBetweenAlerts = Long + .valueOf(SystemProperties.getProperty(EPCommonSystemProperties.HEALTHFAIL_ALERT_EVERY_X_INTERVALS)); + logger.debug(EELFLoggerDelegate.debugLogger, + "Polling health every " + sleepInterval + " milliseconds. Alerting every " + + (sleepInterval * numIntervalsBetweenAlerts) / 1000 + " seconds when component remains down."); + + while (true) { + // + // Get DB status. If down, signal alert once every X intervals. + // + databaseUp = this.checkIfDatabaseUp(); + if (databaseUp == false) { + + if ((numIntervalsDatabaseHasBeenDown % numIntervalsBetweenAlerts) == 0) { + // Write a Log entry that will generate an alert + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeHealthCheckMySqlError); + logger.debug(EELFLoggerDelegate.debugLogger, + "Database down, logging to error log to trigger alert."); + numIntervalsDatabaseHasBeenDown++; + } else { + numIntervalsDatabaseHasBeenDown = 0; + } + } + + dbClusterStatusOk = this.checkClusterStatus(); + if (dbClusterStatusOk == false) { + + if ((numIntervalsClusterNotHealthy % numIntervalsBetweenAlerts) == 0) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeHealthCheckMySqlError); + logger.debug(EELFLoggerDelegate.debugLogger, + "Cluster nodes appear to be down, logging to error log to trigger alert."); + numIntervalsClusterNotHealthy++; + } else { + numIntervalsClusterNotHealthy = 0; + } + } + + dbPermissionsOk = this.checkDatabaseAndPermissions(); + if (dbPermissionsOk == false) { + + if ((numIntervalsDatabasePermissionsIncorrect % numIntervalsBetweenAlerts) == 0) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeHealthCheckMySqlError); + logger.debug(EELFLoggerDelegate.debugLogger, + "Database permissions don't seem correct, logging to error log to trigger alert."); + numIntervalsDatabasePermissionsIncorrect++; + } else { + numIntervalsDatabasePermissionsIncorrect = 0; + } + } + + // + // Get UEB status. Publish a bogus message to EP inbox, if 200 OK + // returned, status is Up. + // If down, signal alert once every X intervals. + // EP will ignore this bogus message. + // + uebUp = this.checkIfUebUp(); + if (uebUp == false) { + + if ((numIntervalsUebHasBeenDown % numIntervalsBetweenAlerts) == 0) { + // Write a Log entry that will generate an alert + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeHealthCheckUebClusterError); + logger.debug(EELFLoggerDelegate.debugLogger, "UEB down, logging to error log to trigger alert"); + numIntervalsUebHasBeenDown++; + } else { + numIntervalsUebHasBeenDown = 0; + } + } + + // The front end should be up because the API is called through + // proxy front end server. + frontEndUp = true; + + // If the rest API called, the backend is always up + backEndUp = true; + + // + // future nice to have...get Partner status + // + // For all apps exposing a rest url, query one of the rest + // urls(/roles?) and manage a list + // of app name/status. We might not return back a non 200 OK in + // health check, but we + // could return information in the json content of a health check. + // + + // + // Get DB status. If down, signal alert once every X intervals. + // + if (Thread.interrupted()) { + logger.info(EELFLoggerDelegate.errorLogger, "==> UebMainHandler exiting"); + break; + } + + try { + Thread.sleep(sleepInterval); + } catch (InterruptedException e) { + logger.error(EELFLoggerDelegate.errorLogger, "monitorEPHealth interrupted", e); + Thread.currentThread().interrupt(); + } + } + } + + @PostConstruct + public void initHealthMonitor() { + + healthMonitorThread = new Thread("EP HealthMonitor thread") { + public void run() { + try { + monitorEPHealth(); + } catch (InterruptedException e) { + logger.debug(EELFLoggerDelegate.debugLogger, "healthMonitorThread interrupted", e); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "healthMonitorThread failed", e); + } + } + }; + if (healthMonitorThread != null) { + healthMonitorThread.start(); + } + } + + @PreDestroy + public void closeHealthMonitor() { + this.healthMonitorThread.interrupt(); + } + + private boolean checkIfDatabaseUp() { + + boolean isUp = false; + + Session localSession = null; + + try { + localSession = sessionFactory.openSession(); + + if (localSession != null) { + + String sql = "select app_name from fn_app where app_id=1"; + Query query = localSession.createSQLQuery(sql); + @SuppressWarnings("unchecked") + List queryList = query.list(); + if (queryList != null) { + isUp = true; + } + localSession.close(); + } + } catch (Exception e) { + logger.debug(EELFLoggerDelegate.debugLogger, "checkIfDatabaseUp failed", e); + isUp = false; + } + + return isUp; + } + + private boolean checkClusterStatus() { + + boolean isUp = false; + + Session localSession = null; + + try { + localSession = sessionFactory.openSession(); + if (localSession != null) { + // If all nodes are unhealthy in a cluster, this will throw an + // exception + String sql = "select * from mysql.user"; + Query query = localSession.createSQLQuery(sql); + @SuppressWarnings("unchecked") + List queryList = query.list(); + if (queryList != null) { + isUp = true; + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "checkClusterStatus failed", e); + if ((e.getCause() != null) && (e.getCause().getMessage() != null)) { + logger.error(EELFLoggerDelegate.errorLogger, + "checkClusterStatus() exception msg = " + e.getCause().getMessage()); + } + isUp = false; + } finally { + if (localSession != null) { + localSession.close(); + } + } + + return isUp; + + } + + private boolean checkDatabaseAndPermissions() { + + boolean isUp = false; + + Session localSession = null; + + try { + localSession = sessionFactory.openSession(); + if (localSession != null) { + String sql = "SHOW GRANTS FOR CURRENT_USER"; + Query query = localSession.createSQLQuery(sql); + @SuppressWarnings("unchecked") + List grantsList = query.list(); + for (String str : grantsList) { + if ((str.toUpperCase().contains("ALL")) + || (str.toUpperCase().contains("DELETE") && str.toUpperCase().contains("SELECT") + && str.toUpperCase().contains("UPDATE") && str.toUpperCase().contains("INSERT"))) { + isUp = true; + break; + } + } + if (isUp == false) { + logger.error(EELFLoggerDelegate.errorLogger, + "checkDatabaseAndPermissions() returning false. SHOW GRANTS FOR CURRENT_USER being dumped:"); + for (String str : grantsList) { + logger.error(EELFLoggerDelegate.errorLogger, "grants output item = [" + str + "]"); + } + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "checkDatabaseAndPermissions failed", e); + if ((e.getCause() != null) && (e.getCause().getMessage() != null)) { + logger.error(EELFLoggerDelegate.errorLogger, + "checkDatabaseAndPermissions() exception msg = " + e.getCause().getMessage()); + } + isUp = false; + } finally { + if (localSession != null) { + localSession.close(); + } + } + + return isUp; + + } + + private boolean checkIfUebUp() { + boolean uebUp = false; + try { + boolean isAvailable = epUebHelper.checkAvailability(); + boolean messageCanBeSent = epUebHelper.MessageCanBeSentToTopic(); + uebUp = (isAvailable && messageCanBeSent); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "checkIfUebUp failed", e); + } + + return uebUp; + + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/UserContextListener.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/UserContextListener.java new file mode 100644 index 00000000..05af7388 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/UserContextListener.java @@ -0,0 +1,51 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.listener; + +import java.util.HashMap; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +@WebListener +public class UserContextListener implements ServletContextListener{ + @SuppressWarnings("rawtypes") + public void contextInitialized(ServletContextEvent event){ + ServletContext context = event.getServletContext(); + // + // instanciate a map to store references to all the active + // sessions and bind it to context scope. + // + HashMap activeUsers = new HashMap(); + context.setAttribute("activeUsers", activeUsers); + } + + /** + * Needed for the ServletContextListener interface. + */ + public void contextDestroyed(ServletContextEvent event){ + // To overcome the problem with losing the session references + // during server restarts, put code here to serialize the + // activeUsers HashMap. Then put code in the contextInitialized + // method that reads and reloads it if it exists... + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/UserSessionListener.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/UserSessionListener.java new file mode 100644 index 00000000..ebab2aa3 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/listener/UserSessionListener.java @@ -0,0 +1,126 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +package org.openecomp.portalapp.portal.listener; + +import java.util.HashMap; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.annotation.WebListener; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.util.StringUtils; + +import org.openecomp.portalsdk.core.domain.support.CollaborateList; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.service.SharedContextService; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; + +/** + * Listens for session-create and session-destroy events. + */ +@WebListener +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class UserSessionListener implements HttpSessionListener { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(UserSessionListener.class); + + /** + * Access to the database + */ + @Autowired + SharedContextService sharedContextService; + + public void init(ServletConfig config) { + } + + /** + * Adds sessions to the context scoped HashMap when they begin. + */ + public void sessionCreated(HttpSessionEvent event) { + HttpSession session = event.getSession(); + ServletContext context = session.getServletContext(); + HashMap activeUsers = (HashMap) context.getAttribute("activeUsers"); + + activeUsers.put(session.getId(), session); + context.setAttribute("activeUsers", activeUsers); + logger.info(EELFLoggerDelegate.debugLogger, "Session Created : " + session.getId()); + } + + /** + * Removes sessions from the context scoped HashMap when they expire or are + * invalidated. + */ + public void sessionDestroyed(HttpSessionEvent event) { + + try { + HttpSession session = event.getSession(); + ServletContext context = session.getServletContext(); + HashMap activeUsers = (HashMap) context.getAttribute("activeUsers"); + activeUsers.remove(session.getId()); + + EPUser user = (EPUser) session + .getAttribute(SystemProperties.getProperty(SystemProperties.USER_ATTRIBUTE_NAME)); + if (user!=null && !StringUtils.isEmpty(user.getOrgUserId())) { + CollaborateList.delUserName(user.getOrgUserId()); + } + + // Remove any shared context entries for this session. + if (getSharedContextService()!=null) { + getSharedContextService().deleteSharedContexts(session.getId()); + + // Clean the shared context each time a session is destroyed. + // TODO: move the threshold to configuration file. + getSharedContextService().expireSharedContexts(60 * 60 * 8); + } + + logger.info(EELFLoggerDelegate.debugLogger, "Session Destroyed : " + session.getId()); + + } catch (Exception e) { + logger.warn(EELFLoggerDelegate.errorLogger, "Exception occurred while executing sessionDestroyed. Details: " + EcompPortalUtils.getStackTrace(e)); + } + } + + private static ApplicationContext applicationContext; + + public static void setApplicationContext(ApplicationContext _applicationContext) { + applicationContext = _applicationContext; + } + + public SharedContextService getSharedContextService() { + if(sharedContextService == null){ + if (applicationContext != null) + sharedContextService = (SharedContextService)applicationContext.getBean("sharedContextService"); + } + + return sharedContextService; + } + + public void setSharedContextService(SharedContextService sharedContextService) { + this.sharedContextService = sharedContextService; + } +} + diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPAuditLog.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPAuditLog.java new file mode 100644 index 00000000..965866c8 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPAuditLog.java @@ -0,0 +1,31 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.logging.aop; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EPAuditLog { + String value() default ""; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPEELFLoggerAdvice.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPEELFLoggerAdvice.java new file mode 100644 index 00000000..89f1b92f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPEELFLoggerAdvice.java @@ -0,0 +1,371 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.logging.aop; + +import java.net.InetAddress; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; + +import javax.servlet.http.HttpServletRequest; + +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.util.EPUserUtils; +import org.openecomp.portalsdk.core.exception.SessionExpiredException; +import org.openecomp.portalsdk.core.logging.format.AlarmSeverityEnum; +import org.openecomp.portalsdk.core.logging.format.AuditLogFormatter; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalsdk.core.util.SystemProperties.SecurityEventTypeEnum; +import org.openecomp.portalsdk.core.web.support.UserUtils; +import org.slf4j.MDC; + +import com.att.eelf.configuration.Configuration; + +@org.springframework.context.annotation.Configuration +public class EPEELFLoggerAdvice { + + private EELFLoggerDelegate adviceLogger = EELFLoggerDelegate.getLogger(EPEELFLoggerAdvice.class); + + /** + * DateTime Format according to the ECOMP Application Logging Guidelines. + */ + private static final SimpleDateFormat ecompLogDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + + /** + * @return Current date and time in the format specified by the ECOMP + * Application Logging Guidelines. + */ + public static String getCurrentDateTimeUTC() { + String currentDateTime = ecompLogDateFormat.format(new Date()); + return currentDateTime; + } + + /** + * Sets logging context with values from HttpServletRequest object. + * + * @param req + * HttpServletRequest + * @param securityEventType + * SecurityEventTypeEnum + */ + public void loadServletRequestBasedDefaults(HttpServletRequest req, SecurityEventTypeEnum securityEventType) { + try { + setHttpRequestBasedDefaultsIntoGlobalLoggingContext(req, securityEventType, req.getServletPath()); + } catch (Exception e) { + adviceLogger.error(EELFLoggerDelegate.errorLogger, "loadServletRequestBasedDefaults failed", e); + } + } + + /** + * + * @param securityEventType + * @param args + * @param passOnArgs + * @return + */ + public Object[] before(SecurityEventTypeEnum securityEventType, Object[] args, Object[] passOnArgs) { + String className = ""; + if (passOnArgs[0] != null) { + className = passOnArgs[0].toString(); + } + + String methodName = ""; + if (passOnArgs[1] != null) { + methodName = passOnArgs[1].toString(); + } + + // Initialize Request defaults only for controller methods. + MDC.put(className + methodName + EPCommonSystemProperties.METRICSLOG_BEGIN_TIMESTAMP, getCurrentDateTimeUTC()); + MDC.put(EPCommonSystemProperties.TARGET_ENTITY, EPCommonSystemProperties.ECOMP_PORTAL_BE); + MDC.put(EPCommonSystemProperties.TARGET_SERVICE_NAME, methodName); + if (securityEventType != null) { + MDC.put(className + methodName + EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP, + getCurrentDateTimeUTC()); + HttpServletRequest req = null; + if (args[0] != null && args[0] instanceof HttpServletRequest) { + req = (HttpServletRequest) args[0]; + this.setHttpRequestBasedDefaultsIntoGlobalLoggingContext(req, securityEventType, methodName); + } + } + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(className); + logger.debug(EELFLoggerDelegate.debugLogger, "EPEELFLoggerAdvice#before: entering {}", methodName); + return new Object[] { "" }; + } + + /** + * + * @param securityEventType + * @param statusCode + * @param responseCode + * @param args + * @param returnArgs + * @param passOnArgs + */ + public void after(SecurityEventTypeEnum securityEventType, String statusCode, String responseCode, Object[] args, + Object[] returnArgs, Object[] passOnArgs) { + String className = ""; + if (passOnArgs[0] != null) + className = passOnArgs[0].toString(); + + // Method Name + String methodName = ""; + if (passOnArgs[1] != null) + methodName = passOnArgs[1].toString(); + + if (MDC.get(EPCommonSystemProperties.TARGET_SERVICE_NAME) == null + || MDC.get(EPCommonSystemProperties.TARGET_SERVICE_NAME) == "") + MDC.put(EPCommonSystemProperties.TARGET_SERVICE_NAME, methodName); + + if (MDC.get(EPCommonSystemProperties.TARGET_ENTITY) == null + || MDC.get(EPCommonSystemProperties.TARGET_ENTITY) == "") + MDC.put(EPCommonSystemProperties.TARGET_ENTITY, EPCommonSystemProperties.ECOMP_PORTAL_BE); + + MDC.put(EPCommonSystemProperties.METRICSLOG_BEGIN_TIMESTAMP, + MDC.get(className + methodName + EPCommonSystemProperties.METRICSLOG_BEGIN_TIMESTAMP)); + MDC.put(EPCommonSystemProperties.METRICSLOG_END_TIMESTAMP, getCurrentDateTimeUTC()); + this.calculateDateTimeDifference(MDC.get(EPCommonSystemProperties.METRICSLOG_BEGIN_TIMESTAMP), + MDC.get(EPCommonSystemProperties.METRICSLOG_END_TIMESTAMP)); + + // Making sure to reload the INCOMING request MDC defaults if they have + // been wiped out by either Outgoing or LDAP Phone book search + // operations. + if (securityEventType != null && args[0] != null && args[0] instanceof HttpServletRequest + && securityEventType == SecurityEventTypeEnum.INCOMING_REST_MESSAGE + && (MDC.get(EPCommonSystemProperties.FULL_URL) == null + || MDC.get(EPCommonSystemProperties.FULL_URL) == "")) { + HttpServletRequest req = (HttpServletRequest) args[0]; + this.setHttpRequestBasedDefaultsIntoGlobalLoggingContext(req, securityEventType, methodName); + } + + // Use external API response code in case if it resulted in an error. + String externalAPIResponseCode = MDC.get(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE); + if (externalAPIResponseCode == null || externalAPIResponseCode == "" + || externalAPIResponseCode.trim().equalsIgnoreCase("200")) { + MDC.put(EPCommonSystemProperties.RESPONSE_CODE, responseCode); + MDC.put(EPCommonSystemProperties.STATUS_CODE, statusCode); + } else { + MDC.put(EPCommonSystemProperties.RESPONSE_CODE, externalAPIResponseCode); + MDC.put(EPCommonSystemProperties.STATUS_CODE, "ERROR"); + } + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(className); + logger.debug(EELFLoggerDelegate.debugLogger, "EPEELFLoggerAdvice#after: finished {}", methodName); + + // Log security message, if necessary + if (securityEventType != null) { + MDC.put(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP, + MDC.get(className + methodName + EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP)); + MDC.put(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP, getCurrentDateTimeUTC()); + this.calculateDateTimeDifference(MDC.get(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP), + MDC.get(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP)); + + this.logSecurityMessage(logger, securityEventType, methodName); + + // Outgoing & LDAP messages are part of Incoming requests so, + // keep "RequestId", "PartnerName", "ServiceName", "LoginId" & + // "ResponseCode" etc. in memory and remove it only when + // finished processing the parent incoming message. + if (securityEventType != SecurityEventTypeEnum.OUTGOING_REST_MESSAGE + && securityEventType != SecurityEventTypeEnum.LDAP_PHONEBOOK_USER_SEARCH) { + MDC.remove(Configuration.MDC_KEY_REQUEST_ID); + MDC.remove(EPCommonSystemProperties.PARTNER_NAME); + MDC.remove(Configuration.MDC_SERVICE_NAME); + MDC.remove(EPCommonSystemProperties.MDC_LOGIN_ID); + MDC.remove(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE); + } + + // clear when finishes audit logging + MDC.remove(EPCommonSystemProperties.FULL_URL); + MDC.remove(EPCommonSystemProperties.PROTOCOL); + MDC.remove(EPCommonSystemProperties.STATUS_CODE); + MDC.remove(className + methodName + EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.AUDITLOG_END_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.RESPONSE_CODE); + } + MDC.remove(className + methodName + EPCommonSystemProperties.METRICSLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.METRICSLOG_BEGIN_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.METRICSLOG_END_TIMESTAMP); + MDC.remove(EPCommonSystemProperties.MDC_TIMER); + MDC.remove(EPCommonSystemProperties.TARGET_ENTITY); + MDC.remove(EPCommonSystemProperties.TARGET_SERVICE_NAME); + } + + /** + * + * @param logger + * @param securityEventType + * @param restMethod + */ + private void logSecurityMessage(EELFLoggerDelegate logger, SecurityEventTypeEnum securityEventType, + String restMethod) { + StringBuilder additionalInfoAppender = new StringBuilder(); + String auditMessage = ""; + + if (securityEventType == SecurityEventTypeEnum.OUTGOING_REST_MESSAGE) { + additionalInfoAppender.append(String.format("%s '%s' request was initiated.", restMethod, + MDC.get(EPCommonSystemProperties.TARGET_SERVICE_NAME))); + } else if (securityEventType == SecurityEventTypeEnum.LDAP_PHONEBOOK_USER_SEARCH) { + additionalInfoAppender.append("LDAP Phonebook search operation is performed."); + } else { + additionalInfoAppender.append(String.format("%s request was received.", restMethod)); + + if (securityEventType == SecurityEventTypeEnum.FE_LOGIN_ATTEMPT) { + String loginId = ""; + String additionalMessage = " Successfully authenticated."; + loginId = MDC.get(EPCommonSystemProperties.MDC_LOGIN_ID); + if (loginId == null || loginId == "" || loginId == EPCommonSystemProperties.UNKNOWN) { + additionalMessage = " No cookies are found."; + } + additionalInfoAppender.append(additionalMessage); + } else if (securityEventType == SecurityEventTypeEnum.FE_LOGOUT) { + additionalInfoAppender.append(" User has been successfully logged out."); + } + } + + String fullURL = MDC.get(EPCommonSystemProperties.FULL_URL); + if (fullURL != null && fullURL != "") { + additionalInfoAppender.append(" Request-URL:" + MDC.get(EPCommonSystemProperties.FULL_URL)); + } + + auditMessage = AuditLogFormatter.getInstance().createMessage(MDC.get(EPCommonSystemProperties.PROTOCOL), + securityEventType.name(), MDC.get(EPCommonSystemProperties.MDC_LOGIN_ID), + additionalInfoAppender.toString()); + + logger.info(EELFLoggerDelegate.auditLogger, auditMessage); + } + + /** + * + * @param req + * @param securityEventType + * @param restMethod + */ + private void setHttpRequestBasedDefaultsIntoGlobalLoggingContext(HttpServletRequest req, + SecurityEventTypeEnum securityEventType, String restMethod) { + /** + * No need to load the request based defaults for the following security + * messages since either they are initiated by the Portal BE or not Http + * request based. + */ + if (req != null) { + if (securityEventType != SecurityEventTypeEnum.OUTGOING_REST_MESSAGE + && securityEventType != SecurityEventTypeEnum.LDAP_PHONEBOOK_USER_SEARCH + && securityEventType != SecurityEventTypeEnum.INCOMING_UEB_MESSAGE) { + // Load the RequestID (aka TrasactionId) into MDC context. + String requestId = UserUtils.getRequestId(req); + if (requestId == "" || requestId == null) { + requestId = UUID.randomUUID().toString(); + } + MDC.put(Configuration.MDC_KEY_REQUEST_ID, requestId); + + // Load user agent into MDC context, if available. + String accessingClient = "Unknown"; + accessingClient = req.getHeader(SystemProperties.USERAGENT_NAME); + if (accessingClient != null && accessingClient != "" && (accessingClient.contains("Mozilla") + || accessingClient.contains("Chrome") || accessingClient.contains("Safari"))) { + accessingClient = EPCommonSystemProperties.ECOMP_PORTAL_FE; + } + MDC.put(EPCommonSystemProperties.PARTNER_NAME, accessingClient); + + // Load loginId into MDC context. + EPUser user = null; + try { + user = EPUserUtils.getUserSession(req); + } catch (SessionExpiredException se) { + adviceLogger.debug(EELFLoggerDelegate.debugLogger, + "setHttpRequestBasedDefaultsIntoGlobalLoggingContext: No user found in session"); + } + + MDC.put(EPCommonSystemProperties.MDC_LOGIN_ID, (user != null ? user.getOrgUserId() : "NoUser")); + + // Rest URL & Protocol + String restURL = ""; + MDC.put(EPCommonSystemProperties.FULL_URL, EPCommonSystemProperties.UNKNOWN); + MDC.put(EPCommonSystemProperties.PROTOCOL, EPCommonSystemProperties.HTTP); + restURL = UserUtils.getFullURL(req); + if (restURL != null && restURL != "") { + MDC.put(EPCommonSystemProperties.FULL_URL, restURL); + if (restURL.toLowerCase().contains("https")) { + MDC.put(EPCommonSystemProperties.PROTOCOL, EPCommonSystemProperties.HTTPS); + } + } + + // Rest Path + MDC.put(Configuration.MDC_SERVICE_NAME, restMethod); + String restPath = req.getServletPath(); + if (restPath != null && restPath != "") { + MDC.put(Configuration.MDC_SERVICE_NAME, restPath); + } + + // Client IPAddress i.e. IPAddress of the remote host who is + // making this request. + String clientIPAddress = ""; + clientIPAddress = req.getHeader("X-FORWARDED-FOR"); + if (clientIPAddress == null) { + clientIPAddress = req.getRemoteAddr(); + } + MDC.put(EPCommonSystemProperties.CLIENT_IP_ADDRESS, clientIPAddress); + } else if (securityEventType == SecurityEventTypeEnum.LDAP_PHONEBOOK_USER_SEARCH) { + MDC.put(EPCommonSystemProperties.TARGET_ENTITY, "Phonebook"); + MDC.put(EPCommonSystemProperties.TARGET_SERVICE_NAME, "search"); + } + } else { + MDC.put(Configuration.MDC_SERVICE_NAME, restMethod); + MDC.put(EPCommonSystemProperties.PARTNER_NAME, EPCommonSystemProperties.ECOMP_PORTAL_FE); + } + + MDC.put(Configuration.MDC_SERVICE_INSTANCE_ID, ""); + MDC.put(Configuration.MDC_ALERT_SEVERITY, AlarmSeverityEnum.INFORMATIONAL.toString()); + try { + MDC.put(Configuration.MDC_SERVER_FQDN, InetAddress.getLocalHost().getHostName()); + MDC.put(Configuration.MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress()); + MDC.put(Configuration.MDC_INSTANCE_UUID, SystemProperties.getProperty(SystemProperties.INSTANCE_UUID)); + } catch (Exception e) { + adviceLogger.error(EELFLoggerDelegate.errorLogger, + "setHttpRequestBasedDefaultsIntoGlobalLoggingContext failed", e); + } + } + + /** + * + * @param beginDateTime + * @param endDateTime + */ + private void calculateDateTimeDifference(String beginDateTime, String endDateTime) { + if (beginDateTime != null && endDateTime != null) { + try { + Date beginDate = ecompLogDateFormat.parse(beginDateTime); + Date endDate = ecompLogDateFormat.parse(endDateTime); + String timeDifference = String.format("%d", endDate.getTime() - beginDate.getTime()); + MDC.put(SystemProperties.MDC_TIMER, timeDifference); + } catch (Exception e) { + adviceLogger.error(EELFLoggerDelegate.errorLogger, "calculateDateTimeDifference failed", e); + } + } + } + + public String getInternalResponseCode() { + return MDC.get(EPCommonSystemProperties.RESPONSE_CODE); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPMetricsLog.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPMetricsLog.java new file mode 100644 index 00000000..ab8a3672 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/aop/EPMetricsLog.java @@ -0,0 +1,31 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.logging.aop; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EPMetricsLog { + String value() default ""; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/ApplicationCodes.properties b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/ApplicationCodes.properties new file mode 100644 index 00000000..6ee63b24 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/ApplicationCodes.properties @@ -0,0 +1,141 @@ +### +# ================================================================================ +# ECOMP Portal +# ================================================================================ +# Copyright (C) 2017 AT&T Intellectual Property +# ================================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ================================================================================ +### +######################################################################## +#Resource key=Error Code|Message text|Resolution text |Description text +####### +#Newlines can be utilized to add some clarity ensuring continuing line +#has atleast one leading space +#ResourceKey=\ +# ERR0000E\ +# Sample error msg txt\ +# Sample resolution msg\ +# Sample description txt +# +###### +#Error code classification category +#100 Permission errors +#200 Availability errors/Timeouts +#300 Data errors +#400 Schema Interface type/validation errors +#500 Business process errors +#900 Unknown errors +# +######################################################################## + +# Define Alarm Codes Specific to ECOMP Portal +BEUEBAUTHENTICATIONERROR_ONE_ARGUMENT= ||Reason: {0}.|\ + An Authentication failure occurred during access to UEB server. Please check that UEB keys are configured correctly under fusion.properties file. + +BERESTAPIAUTHENTICATIONERROR = |||Please check application credentials defined in Database or portal.properties file. + +INTERNALAUTHENTICATIONINFO_ONE_ARGUMENT=||Description: {0}.|Please check the logs for more information. + +INTERNALAUTHENTICATIONWARNING_ONE_ARGUMENT=||Description: {0}.|Please check the logs for more information. + +INTERNALAUTHENTICATIONERROR_ONE_ARGUMENT=||Description: {0}.|Please check the logs for more information. + +INTERNALAUTHENTICATIONFATAL_ONE_ARGUMENT=||Description: {0}.|Please check the logs for more information. + +BEHEALTHCHECKERROR= |||Please check the logs for more information. + +BEHEALTHCHECKMYSQLERROR= ||Please check the logs for more information.|\ + Check connectivity to MYSQL is configured correctly under system.properties file. + +BEHEALTHCHECKUEBCLUSTERERROR= ||Please check the logs for more information.|\ + Check connectivity to UEB cluster which is configured under portal.properties file. + +FEHEALTHCHECKERROR= |||Please check connectivity from this FE instance towards BE or BE Load Balancer. + +BEHEALTHCHECKRECOVERY= |||Please check logs for more specific information about the problem. + +BEHEALTHCHECKMYSQLRECOVERY= |||Please check logs for more specific information about the problem. + +BEHEALTHCHECKUEBCLUSTERRECOVERY= |||Please check logs for more specific information about the problem. + +FEHEALTHCHECKRECOVERY= |||Please check logs for more specific information about the problem. + +#UEB communication +BEUEBCONNECTIONERROR_ONE_ARGUMENT= ||Reason: {0}.|\ + Please check UEB server list and keys configured under Portal.Properties file. + +BEUEBUNKOWNHOSTERROR_ONE_ARGUMENT= ||Cannot reach host {0}.|\ + Please check UEB server list and keys configured under Portal.Properties file. + +#Onboarding apps +BEUEBREGISTERONBOARDINGAPPERROR= ||Reason: {0}.|\ + Please check UEB server list and keys configured under Portal.Properties file. + +#HTTP communication +BEHTTPCONNECTIONERROR_ONE_ARGUMENT= ||Reason: {0}.|Please check the logs for more information. + +INTERNALCONNECTIONINFO_ONE_ARGUMENT= ||Description: {0}.|Please check the logs for more information. + +INTERNALCONNECTIONWARNING_ONE_ARGUMENT= ||Description: {0}.|Please check the logs for more information. + +INTERNALCONNECTIONERROR_ONE_ARGUMENT= ||Description: {0}.|Please check the logs for more information. + +INTERNALCONNECTIONFATAL_ONE_ARGUMENT= ||Description: {0}.|Please check the logs for more information. + +BEUEBOBJECTNOTFOUNDERROR_ONE_ARGUMENT= ||Data not found: {0}.|\ + An error occurred during access to UEB Server, {1} failed to either register or unregister to/from UEB topic. + +#Login error codes +BEUSERMISSINGERROR_ONE_ARGUMENT= |||\ + User {0} must be added to the corresponding application with proper user roles. + +BEUSERINACTIVEWARNING_ONE_ARGUMENT= |||\ + User {0} must be added to the corresponding application with proper user roles. + +BEUSERADMINPRIVILEGESINFO_ONE_ARGUMENT= |||\ + User {0} should be given proper administrator role for the corresponding application to perform the necessary actions. + +BEINVALIDJSONINPUT= |||Please check error logs for more information. + +BEINCORRECTHTTPSTATUSERROR= |||Please check logs for more information. + +BEINITIALIZATIONERROR= |||Please check logs for more information. + +BEUEBSYSTEMERROR= ||\ + Operation: {0}.|\ + An error occurred in {1} distribution mechanism. Please check the logs for more information. + +BEDAOSYSTEMERROR= |||Please check MySQL DB health or look at the logs for more details. + +BESYSTEMERROR= |||Please check logs for more information. + +BEEXECUTEROLLBACKERROR= |||Please check MYSQL DB health or look at the logs for more details. + +FEHTTPLOGGINGERROR= |||Please check MYSQL DB health or look at the logs for more details. + +FEPORTALSERVLETERROR= |||Please check logs for more specific information about the problem. + +BEDAOCLOSESESSIONERROR= |||Please check MYSQL DB health or look at the logs form more details. + +BERESTAPIGENERALERROR= |||Please check error log for more information. + +FEHEALTHCHECKGENERALERROR= |||Please check error log for more information. + +INTERNALUNEXPECTEDINFO_ONE_ARGUMENT= |||Description: {0}. + +INTERNALUNEXPECTEDWARNING_ONE_ARGUMENT= |||Description: {0}. + +INTERNALUNEXPECTEDERROR_ONE_ARGUMENT= |||Description: {0}. + +INTERNALUNEXPECTEDFATAL_ONE_ARGUMENT= |||Description: {0}. diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/EPAppMessagesEnum.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/EPAppMessagesEnum.java new file mode 100644 index 00000000..a9cb2e4c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/EPAppMessagesEnum.java @@ -0,0 +1,260 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.logging.format; + +import org.openecomp.portalsdk.core.logging.format.AlarmSeverityEnum; +import org.openecomp.portalsdk.core.logging.format.ErrorSeverityEnum; +import org.openecomp.portalsdk.core.logging.format.ErrorTypeEnum; + +/** + * + * @author rc580q + * Add ECOMP Portal Specific Error Code Enums here, for generic + * ones (ones you think are useful not only Portal but also SDK), add it + * to the enum class AppMessagesEnum defined in SDK. + */ +public enum EPAppMessagesEnum { + /* + 100-199 Security/Permission Related + - Authentication problems (from external client, to external server) + - Certification errors + - + + 200-299 Availability/Timeout Related + - connectivity error + - connection timeout + + 300-399 Data Access/Integrity Related + - Data in graph in invalid(E.g. no creator is found for service) + - Artifact is missing in ES, but exists in graph. + + 400-499 Schema Interface Type/Validation + - received Pay-load checksum is invalid + - received JSON is not valid + + 500-599 Business/Flow Processing Related + - check out to service is not allowed + - Roll-back is done + - failed to generate heat file + + + 600-899 Reserved - do not use + + 900-999 Unknown Errors + - Unexpected exception + */ + + BeUebAuthenticationError(EPErrorCodesEnum.BEUEBAUTHENTICATIONERROR_ONE_ARGUMENT, ErrorTypeEnum.AUTHENTICATION_PROBLEM, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR100E", "An Authentication failure occurred during access to UEB server", "Details: {0}.", "Please check UEB server list and keys configured under Portal.Properties file."), + + BeRestApiAuthenticationError(EPErrorCodesEnum.BERESTAPIAUTHENTICATIONERROR, ErrorTypeEnum.AUTHENTICATION_PROBLEM, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR101E", "Rejected an incoming REST API request due to invalid credentials", "", "Please check application credentials defined in Database or properties files."), + + InternalAuthenticationInfo(EPErrorCodesEnum.INTERNALAUTHENTICATIONINFO_ONE_ARGUMENT, ErrorTypeEnum.AUTHENTICATION_PROBLEM, AlarmSeverityEnum.INFORMATIONAL, ErrorSeverityEnum.INFO, + "ERR199I", "Internal authentication problem", "Details: {0}.", "Please check the logs for more information."), + + InternalAuthenticationWarning(EPErrorCodesEnum.INTERNALAUTHENTICATIONWARNING_ONE_ARGUMENT, ErrorTypeEnum.AUTHENTICATION_PROBLEM, AlarmSeverityEnum.MINOR, ErrorSeverityEnum.WARN, + "ERR199W", "Internal authentication problem", "Details: {0}.", "Please check the logs for more information."), + + InternalAuthenticationError(EPErrorCodesEnum.INTERNALAUTHENTICATIONERROR_ONE_ARGUMENT, ErrorTypeEnum.AUTHENTICATION_PROBLEM, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR199E", "Internal authentication problem", "Details: {0}.", "Please check the logs for more information."), + + InternalAuthenticationFatal(EPErrorCodesEnum.INTERNALAUTHENTICATIONFATAL_ONE_ARGUMENT, ErrorTypeEnum.AUTHENTICATION_PROBLEM, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.FATAL, + "ERR199F", "Internal authentication problem", "Details: {0}.", "Please check the logs for more information."), + + BeHealthCheckError(EPErrorCodesEnum.BeHEALTHCHECKERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.ERROR, + "ERR200E", "ECOMP-PORTAL Back-end probably lost connectivity to either one of the following components: MySQL DB, UEB Cluster", "", "Please check the logs for more information."), + + BeHealthCheckMySqlError(EPErrorCodesEnum.BEHEALTHCHECKMYSQLERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.ERROR, + "ERR201E", "ECOMP-PORTAL Back-end probably lost connectivity to MySQL DB", "", "Check connectivity to MYSQL is configured correctly under system.properties file."), + + BeHealthCheckUebClusterError(EPErrorCodesEnum.BEHEALTHCHECKUEBCLUSTERERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.ERROR, + "ERR203E", "ECOMP-PORTAL Back-end probably lost connectivity to UEB Cluster", "", "Check connectivity to UEB cluster which is configured under portal.properties file."), + + FeHealthCheckError(EPErrorCodesEnum.FEHEALTHCHECKERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.ERROR, + "ERR204E", "Unable to connect to a valid ECOMP-PORTAL Back-end Server.", "", "Please check connectivity from this FE instance towards BE or BE Load Balancer."), + + BeHealthCheckRecovery(EPErrorCodesEnum.BEHEALTHCHECKRECOVERY, ErrorTypeEnum.RECOVERY, AlarmSeverityEnum.INFORMATIONAL, ErrorSeverityEnum.INFO, + "ERR205I", "ECOMP-PORTAL Back-end Recovery to either one of the following components: MySQL DB, UEB Cluster", "", "Please check logs for more specific information about the problem."), + + BeHealthCheckMySqlRecovery(EPErrorCodesEnum.BEHEALTHCHECKMYSQLRECOVERY, ErrorTypeEnum.RECOVERY, AlarmSeverityEnum.INFORMATIONAL, ErrorSeverityEnum.INFO, + "ERR206I", "ECOMP-PORTAL Back-end connection recovery to MySQL DB", "", "Please check logs for more specific information about the problem."), + + BeHealthCheckUebClusterRecovery(EPErrorCodesEnum.BEHEALTHCHECKUEBCLUSTERRECOVERY, ErrorTypeEnum.RECOVERY, AlarmSeverityEnum.INFORMATIONAL, ErrorSeverityEnum.INFO, + "ERR208I", "ECOMP-PORTAL Back-end connection recovery to UEB Cluster", "", "Please check logs for more specific information about the problem."), + + FeHealthCheckRecovery(EPErrorCodesEnum.FEHEALTHCHECKRECOVERY, ErrorTypeEnum.RECOVERY, AlarmSeverityEnum.INFORMATIONAL, ErrorSeverityEnum.INFO, + "ERR209I", "Connectivity to ECOMP-PORTAL Front-end Server is recovered", "", "Please check logs for more specific information about the problem."), + + BeUebConnectionError(EPErrorCodesEnum.BEUEBCONNECTIONERROR_ONE_ARGUMENT, ErrorTypeEnum.CONNECTION_PROBLEM, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR210E", "ECOMP-PORTAL Back-end probably lost connectivity to UEB Cluster", "Details: {0}.", "Please check UEB server list and keys configured under Portal.Properties file."), + + BeUebUnkownHostError(EPErrorCodesEnum.BEUEBUNKOWNHOSTERROR_ONE_ARGUMENT, ErrorTypeEnum.CONNECTION_PROBLEM, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR211E", "ECOMP-PORTAL Back-end probably lost connectivity to UEB Cluster", "Cannot reach host: {0}.", "Please check UEB server list and keys configured under Portal.Properties file."), + + BeUebRegisterOnboardingAppError(EPErrorCodesEnum.BEUEBREGISTERONBOARDINGAPPERROR, ErrorTypeEnum.CONNECTION_PROBLEM, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR212E", "Failed to register the On-boarding application with UEB Communication server", "Details: {0}.", "Please check UEB server list and keys configured under Portal.Properties file."), + + BeHttpConnectionError(EPErrorCodesEnum.BEHTTPCONNECTIONERROR_ONE_ARGUMENT, ErrorTypeEnum.CONNECTION_PROBLEM, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR213E", "It could be that communication to an external application might resulted an exception or failed to reach the external application", + "Details: {0}.", "Please check logs for more information."), + + InternalConnectionInfo(EPErrorCodesEnum.INTERNALCONNECTIONINFO_ONE_ARGUMENT, ErrorTypeEnum.CONNECTION_PROBLEM, AlarmSeverityEnum.INFORMATIONAL, ErrorSeverityEnum.INFO, + "ERR299I", "Internal Connection problem", "Details: {0}.", "Please check logs for more information."), + + InternalConnectionWarning(EPErrorCodesEnum.INTERNALCONNECTIONWARNING_ONE_ARGUMENT, ErrorTypeEnum.CONNECTION_PROBLEM, AlarmSeverityEnum.MINOR, ErrorSeverityEnum.WARN, + "ERR299W", "Internal Connection problem", "Details: {0}.", "Please check logs for more information."), + + InternalConnectionError(EPErrorCodesEnum.INTERNALCONNECTIONERROR_ONE_ARGUMENT, ErrorTypeEnum.CONNECTION_PROBLEM, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR299E", "Internal Connection problem", "Details: {0}.", "Please check logs for more information."), + + InternalConnectionFatal(EPErrorCodesEnum.INTERNALCONNECTIONFATAL_ONE_ARGUMENT, ErrorTypeEnum.CONNECTION_PROBLEM, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.FATAL, + "ERR299F", "Internal Connection problem", "Details: {0}.", "Please check logs for more information."), + + BeUebObjectNotFoundError(EPErrorCodesEnum.BEUEBOBJECTNOTFOUNDERROR_ONE_ARGUMENT, ErrorTypeEnum.DATA_ERROR, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR303E", "Error occurred during access to U-EB Server.", "Data not found: {0}.", "An error occurred during access to UEB Server, {1} failed to either register or unregister to/from UEB topic."), + + BeUserMissingError(EPErrorCodesEnum.BEUSERMISSINGERROR_ONE_ARGUMENT, ErrorTypeEnum.DATA_ERROR, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR310E", "User is not found", "", "User {0} must be added to the corresponding application with proper user roles."), + + BeUserInactiveWarning(EPErrorCodesEnum.BEUSERINACTIVEWARNING_ONE_ARGUMENT, ErrorTypeEnum.DATA_ERROR, AlarmSeverityEnum.MINOR, ErrorSeverityEnum.WARN, + "ERR313W", "User is found but in-active", "", "User {0} must be added to the corresponding application with proper user roles."), + + BeUserAdminPrivilegesInfo(EPErrorCodesEnum.BEUSERADMINPRIVILEGESINFO_ONE_ARGUMENT, ErrorTypeEnum.DATA_ERROR, AlarmSeverityEnum.MINOR, ErrorSeverityEnum.WARN, + "ERR314W", "User is found but don't have administrative privileges", "", "User {0} should be given administrator role for the corresponding application to perform the necessary actions."), + + BeInvalidJsonInput(EPErrorCodesEnum.BEINVALIDJSONINPUT, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR405E", "Failed to convert JSON input to object", "", "Please check logs for more information."), + + BeIncorrectHttpStatusError(EPErrorCodesEnum.BEINCORRECTHTTPSTATUSERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR407E", "Communication to an external application is resulted in with Incorrect Http response code", "", "Please check logs for more information."), + + BeInitializationError(EPErrorCodesEnum.BEINITIALIZATIONERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.ERROR, + "ERR500E", "ECOMP-PORTAL Back-end was not initialized properly", "", "Please check logs for more information."), + + BeUebSystemError(EPErrorCodesEnum.BEUEBSYSTEMERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR502E", "Error occurred during access to U-EB Server", "Details: {0}.", "An error occurred in {1} distribution mechanism. Please check the logs for more information."), + + BeDaoSystemError(EPErrorCodesEnum.BEDAOSYSTEMERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.ERROR, + "ERR505E", "Performing DDL or DML operations on database might have failed", "", "Please check MySQL DB health or look at the logs for more details."), + + BeSystemError(EPErrorCodesEnum.BESYSTEMERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.ERROR, + "ERR506E", "Unexpected error during operation", "", "Please check logs for more information."), + + BeExecuteRollbackError(EPErrorCodesEnum.BEEXECUTEROLLBACKERROR, ErrorTypeEnum.DATA_ERROR, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR507E", "Roll-back operation towards database has failed", "", "Please check MYSQL DB health or look at the logs for more details."), + + FeHttpLoggingError(EPErrorCodesEnum.FEHTTPLOGGINGERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.MINOR, ErrorSeverityEnum.ERROR, + "ERR517E", "Error when logging FE HTTP request/response", "", "Please check MYSQL DB health or look at the logs for more details."), + + FePortalServletError(EPErrorCodesEnum.FEPORTALSERVLETERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR518E", "Error when trying to access FE Portal page.", "", "Please check logs for more information."), + + BeDaoCloseSessionError(EPErrorCodesEnum.BEDAOCLOSESESSIONERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR519E", "Close local session operation with database failed", "", "Please check MYSQL DB health or look at the logs form more details."), + + BeRestApiGeneralError(EPErrorCodesEnum.BERESTAPIGENERALERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.ERROR, + "ERR900E", "Unexpected error during ECOMP-PORTAL Back-end REST API execution", "", "Please check error log for more information."), + + FeHealthCheckGeneralError(EPErrorCodesEnum.FEHEALTHCHECKGENERALERROR, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.ERROR, + "ERR901E", "General error during FE Health Check", "", "Please check error log for more information."), + + InternalUnexpectedInfo(EPErrorCodesEnum.INTERNALUNEXPECTEDINFO_ONE_ARGUMENT, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.INFORMATIONAL, ErrorSeverityEnum.INFO, + "ERR999I", "Unexpected error", "Details: {0}.", "Please check logs for more information."), + + InternalUnexpectedWarning(EPErrorCodesEnum.INTERNALUNEXPECTEDWARNING_ONE_ARGUMENT, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.MINOR, ErrorSeverityEnum.WARN, + "ERR999W", "Unexpected error", "Details: {0}.", "Please check logs for more information."), + + InternalUnexpectedError(EPErrorCodesEnum.INTERNALUNEXPECTEDERROR_ONE_ARGUMENT, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.MAJOR, ErrorSeverityEnum.ERROR, + "ERR999E", "Unexpected error", "Details: {0}.", "Please check logs for more information."), + + InternalUnexpectedFatal(EPErrorCodesEnum.INTERNALUNEXPECTEDFATAL_ONE_ARGUMENT, ErrorTypeEnum.SYSTEM_ERROR, AlarmSeverityEnum.CRITICAL, ErrorSeverityEnum.FATAL, + "ERR999F", "Unexpected error", "Details: {0}.", "Please check logs for more information."), + + ; + + ErrorTypeEnum eType; + AlarmSeverityEnum alarmSeverity; + EPErrorCodesEnum messageCode; + ErrorSeverityEnum errorSeverity; + String errorCode; + String errorDescription; + String details; + String resolution; + + EPAppMessagesEnum(EPErrorCodesEnum messageCode, ErrorTypeEnum eType, AlarmSeverityEnum alarmSeverity, ErrorSeverityEnum errorSeverity, String errorCode, String errorDescription, + String details, String resolution) { + this.messageCode = messageCode; + this.eType = eType; + this.alarmSeverity = alarmSeverity; + this.errorSeverity = errorSeverity; + this.errorCode = errorCode; + this.errorDescription = errorDescription; + this.details = details; + this.resolution = resolution; + } + + public String getDetails() { + return this.details; + } + + public String getResolution() { + return this.resolution; + } + public String getErrorCode() { + return this.errorCode; + } + + public String getErrorDescription() { + return this.errorDescription; + } + + public ErrorSeverityEnum getErrorSeverity() { + return this.errorSeverity; + } + + public void setErrorSeverity(ErrorSeverityEnum errorSeverity) { + this.errorSeverity = errorSeverity; + } + + public EPErrorCodesEnum getMessageCode() { + return messageCode; + } + + public void setMessageCode(EPErrorCodesEnum messageCode) { + this.messageCode = messageCode; + } + + public AlarmSeverityEnum getAlarmSeverity() { + return alarmSeverity; + } + + public void setAlarmSeverity(AlarmSeverityEnum alarmSeverity) { + this.alarmSeverity = alarmSeverity; + } + + public ErrorTypeEnum getErrorType() { + return eType; + } + + public void setErrorType(ErrorTypeEnum eType) { + this.eType = eType; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/EPErrorCodesEnum.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/EPErrorCodesEnum.java new file mode 100644 index 00000000..cbfb4fa3 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/format/EPErrorCodesEnum.java @@ -0,0 +1,95 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.logging.format; + +import com.att.eelf.i18n.EELFResolvableErrorEnum; +import com.att.eelf.i18n.EELFResourceManager; + +/** + * + * @author rc580q + * Add ECOMP Portal Specific Error Code Enums here, for generic + * ones (ones you think are useful not only Portal but also SDK), add it + * to the enum class AppMessagesEnum defined in SDK. + */ +public enum EPErrorCodesEnum implements EELFResolvableErrorEnum { + BERESTAPIAUTHENTICATIONERROR, + BEHTTPCONNECTIONERROR_ONE_ARGUMENT, + BEUEBAUTHENTICATIONERROR_ONE_ARGUMENT, + + INTERNALAUTHENTICATIONINFO_ONE_ARGUMENT, + INTERNALAUTHENTICATIONWARNING_ONE_ARGUMENT, + INTERNALAUTHENTICATIONERROR_ONE_ARGUMENT, + INTERNALAUTHENTICATIONFATAL_ONE_ARGUMENT, + + BEHEALTHCHECKRECOVERY, + BEHEALTHCHECKMYSQLRECOVERY, + BEHEALTHCHECKUEBCLUSTERRECOVERY, + FEHEALTHCHECKRECOVERY, + BeHEALTHCHECKERROR, + + BEHEALTHCHECKMYSQLERROR, + BEHEALTHCHECKUEBCLUSTERERROR, + FEHEALTHCHECKERROR, + BEUEBCONNECTIONERROR_ONE_ARGUMENT, + BEUEBUNKOWNHOSTERROR_ONE_ARGUMENT, + BEUEBREGISTERONBOARDINGAPPERROR, + + INTERNALCONNECTIONINFO_ONE_ARGUMENT, + INTERNALCONNECTIONWARNING_ONE_ARGUMENT, + INTERNALCONNECTIONERROR_ONE_ARGUMENT, + INTERNALCONNECTIONFATAL_ONE_ARGUMENT, + + BEUEBOBJECTNOTFOUNDERROR_ONE_ARGUMENT, + BEUSERMISSINGERROR_ONE_ARGUMENT, + + BEUSERINACTIVEWARNING_ONE_ARGUMENT, + BEUSERADMINPRIVILEGESINFO_ONE_ARGUMENT, + + BEINVALIDJSONINPUT, + BEINCORRECTHTTPSTATUSERROR, + + BEINITIALIZATIONERROR, + BEUEBSYSTEMERROR, + BEDAOSYSTEMERROR, + BESYSTEMERROR, + BEEXECUTEROLLBACKERROR, + + FEHTTPLOGGINGERROR, + FEPORTALSERVLETERROR, + BEDAOCLOSESESSIONERROR, + + BERESTAPIGENERALERROR, + FEHEALTHCHECKGENERALERROR, + + INTERNALUNEXPECTEDINFO_ONE_ARGUMENT, + INTERNALUNEXPECTEDWARNING_ONE_ARGUMENT, + INTERNALUNEXPECTEDERROR_ONE_ARGUMENT, + INTERNALUNEXPECTEDFATAL_ONE_ARGUMENT, + ; + + /** + * Static initializer to ensure the resource bundles for this class are loaded... + * Here this application loads messages from three bundles + */ + static { + EELFResourceManager.loadMessageBundle("org/openecomp/portalapp/portal/logging/format/ApplicationCodes"); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/logic/EPLogUtil.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/logic/EPLogUtil.java new file mode 100644 index 00000000..aa86765b --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/logging/logic/EPLogUtil.java @@ -0,0 +1,270 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.logging.logic; + +import static com.att.eelf.configuration.Configuration.MDC_ALERT_SEVERITY; + +import java.text.MessageFormat; + +import org.slf4j.MDC; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import org.openecomp.portalsdk.core.logging.format.AlarmSeverityEnum; +import org.openecomp.portalsdk.core.logging.format.ErrorSeverityEnum; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.web.support.UserUtils; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; + +public class EPLogUtil { + + // This class has no logger. It uses loggers passed to it. + + /** + * Formats and writes a message to the error log with the class name and the + * specified parameters, using log level info, warn or error appropriate for + * the specified severity + * + * @param classLogger + * Logger for the class where the error occurred; the logger + * carries the class name. + * @param epMessageEnum + * Enum carrying alarm and error severity + * @param param + * Values used to build the message. + */ + public static void logEcompError(EELFLoggerDelegate classLogger, EPAppMessagesEnum epMessageEnum, String... param) { + logEcompError(classLogger, epMessageEnum, null, param); + } + + /** + * Formats and writes a message to the error log with the class name and the + * specified parameters, using log level info, warn or error appropriate for + * the specified severity + * + * @param classLogger + * Logger for the class where the error occurred; the logger + * carries the class name. + * @param epMessageEnum + * Enum carrying alarm and error severity + * @param param + * Values used to build the message. + */ + private static EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); + public static void logEcompError(EPAppMessagesEnum epMessageEnum, String... param) { + try { + AlarmSeverityEnum alarmSeverityEnum = epMessageEnum.getAlarmSeverity(); + ErrorSeverityEnum errorSeverityEnum = epMessageEnum.getErrorSeverity(); + + MDC.put(MDC_ALERT_SEVERITY, alarmSeverityEnum.name()); + MDC.put("ErrorCode", epMessageEnum.getErrorCode()); + MDC.put("ErrorDescription", epMessageEnum.getErrorDescription()); + MDC.put("ClassName", EPLogUtil.class.getName()); + + String resolution = EPLogUtil.formatMessage(epMessageEnum.getDetails() + " " + epMessageEnum.getResolution(), (Object[]) param); + if (errorSeverityEnum == ErrorSeverityEnum.WARN) { + errorLogger.warn(resolution); + } else if(errorSeverityEnum == ErrorSeverityEnum.INFO) { + errorLogger.info(resolution); + } else { + errorLogger.error(resolution); + } + } catch(Exception e) { + errorLogger.error("Failed to log the error code. Details: " + UserUtils.getStackTrace(e)); + } finally { + MDC.remove("ErrorCode"); + MDC.remove("ErrorDescription"); + MDC.remove("ClassName"); + MDC.remove(MDC_ALERT_SEVERITY); + } + } + + /** + * Formats and writes a message to the error log with the class name, + * throwable and the specified parameters, using log level info, warn or + * error appropriate for the specified severity + * + * @param classLogger + * Logger for the class where the error occurred; the logger + * carries the class name. + * @param epMessageEnum + * Enum carrying alarm and error severity + * @param th + * Throwable; ignored if null + * @param param + * Array of Strings used to build the message. + */ + @SuppressWarnings("static-access") + public static void logEcompError(EELFLoggerDelegate classLogger, EPAppMessagesEnum epMessageEnum, Throwable th, + String... param) { + + AlarmSeverityEnum alarmSeverityEnum = epMessageEnum.getAlarmSeverity(); + ErrorSeverityEnum errorSeverityEnum = epMessageEnum.getErrorSeverity(); + + MDC.put(MDC_ALERT_SEVERITY, alarmSeverityEnum.name()); + MDC.put("ErrorCode", epMessageEnum.getErrorCode()); + MDC.put("ErrorDescription", epMessageEnum.getErrorDescription()); + + final String message = EPLogUtil.formatMessage(epMessageEnum.getDetails() + " " + epMessageEnum.getResolution(), + (Object[]) param); + if (errorSeverityEnum == ErrorSeverityEnum.INFO) { + if (th == null) + classLogger.info(classLogger.errorLogger, message); + else + classLogger.info(classLogger.errorLogger, message, th); + } else if (errorSeverityEnum == ErrorSeverityEnum.WARN) { + if (th == null) + classLogger.warn(classLogger.errorLogger, message); + else + classLogger.warn(classLogger.errorLogger, message, th); + } else { + if (th == null) + classLogger.error(classLogger.errorLogger, message); + else + classLogger.error(classLogger.errorLogger, message, th); + } + + // Clean up + MDC.remove(MDC_ALERT_SEVERITY); + MDC.remove("ErrorCode"); + MDC.remove("ErrorDescription"); + } + + /** + * Builds a string using the format and parameters. + * @param message + * @param args + * @return + */ + private static String formatMessage(String message, Object... args) { + StringBuilder sbFormattedMessage = new StringBuilder(); + if (args != null && args.length > 0 && message != null && message != "") { + MessageFormat mf = new MessageFormat(message); + sbFormattedMessage.append(mf.format(args)); + } else { + sbFormattedMessage.append(message); + } + return sbFormattedMessage.toString(); + } + + /** + * Builds a comma-separated string of values to document a user action. + * + * @param action + * @param activity + * @param userId + * @param affectedId + * @param comment + * @return Value suitable for writing to the audit log file. + */ + public static String formatAuditLogMessage(String action, String activity, String userId, String affectedId, + String comment) { + StringBuilder auditLogMsg = new StringBuilder(); + auditLogMsg.append("Click_A:["); + if (action != null && !action.equals("")) { + auditLogMsg.append(" Action: "); + auditLogMsg.append(action); + } + + if (activity != null && !activity.equals("")) { + auditLogMsg.append(",Activity CD: "); + auditLogMsg.append(activity); + } + + if (userId != null && !userId.equals("")) { + auditLogMsg.append(",User ID: "); + auditLogMsg.append(userId); + } + + if (affectedId != null && !affectedId.equals("")) { + auditLogMsg.append(",Affected ID: "); + auditLogMsg.append(affectedId); + } + + if (comment != null && !comment.equals("")) { + auditLogMsg.append(",Comment: "); + auditLogMsg.append(comment); + } + auditLogMsg.append("]"); + return auditLogMsg.toString(); + } + + /** + * Builds a comma-separated string of values to document a user browser + * action. + * + * @param orgUserId + * @param action + * @param activity + * @param actionLink + * @param page + * @param function + * @param type + * @return Value suitable for writing to the audit log file. + */ + public static String formatStoreAnalyticsAuditLogMessage(String orgUserId, String appName, String action, String activity, + String actionLink, String page, String function, String type) { + StringBuilder auditLogStoreAnalyticsMsg = new StringBuilder(); + auditLogStoreAnalyticsMsg.append("Click_Analytics:["); + if (orgUserId != null && !orgUserId.equals("")) { + auditLogStoreAnalyticsMsg.append(" Organization User ID: "); + auditLogStoreAnalyticsMsg.append(orgUserId); + } + + if (appName != null && !appName.equals("")) { + auditLogStoreAnalyticsMsg.append(",AppName: "); + auditLogStoreAnalyticsMsg.append(appName); + } + + if (action != null && !action.equals("")) { + auditLogStoreAnalyticsMsg.append(",Action: "); + auditLogStoreAnalyticsMsg.append(action); + } + + if (activity != null && !activity.equals("")) { + auditLogStoreAnalyticsMsg.append(",Activity: "); + auditLogStoreAnalyticsMsg.append(activity); + } + + if (actionLink != null && !actionLink.equals("")) { + auditLogStoreAnalyticsMsg.append(",ActionLink: "); + auditLogStoreAnalyticsMsg.append(actionLink); + } + + if (page != null && !page.equals("")) { + auditLogStoreAnalyticsMsg.append(",Page: "); + auditLogStoreAnalyticsMsg.append(page); + } + + if (function != null && !function.equals("")) { + auditLogStoreAnalyticsMsg.append(",Function: "); + auditLogStoreAnalyticsMsg.append(function); + } + + if (type != null && !type.equals("")) { + auditLogStoreAnalyticsMsg.append(",Type: "); + auditLogStoreAnalyticsMsg.append(type); + } + auditLogStoreAnalyticsMsg.append("]"); + return auditLogStoreAnalyticsMsg.toString(); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AdminRolesService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AdminRolesService.java new file mode 100644 index 00000000..a1016e1c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AdminRolesService.java @@ -0,0 +1,56 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.EPRole; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.transport.AppsListWithAdminRole; + +public interface AdminRolesService { + + public AppsListWithAdminRole getAppsWithAdminRoleStateForUser(String orgUserId); + + public boolean setAppsWithAdminRoleStateForUser(AppsListWithAdminRole newAppsListWithAdminRoles); + + /** + * Attention! User roles in ECOMP PORTAL cannot be managed by this function. + * @param user + * @return 'true' if user has Super Administrator role SYS_ADMIN_ROLE_ID (1 for now) in ECOMP PORTAL, 'false' otherwise + */ + public boolean isSuperAdmin(EPUser user); + + /** + * Attention! User roles in ECOMP PORTAL cannot be managed by this function. + * @param user + * @return 'true' if user has Account Administrator role ACCOUNT_ADMIN_ROLE_ID (999 for now) for any application except ECOMP Portal, 'false' otherwise + */ + public boolean isAccountAdmin(EPUser user); + + /** + * Attention! User roles in ECOMP PORTAL cannot be managed by this function. + * @param user + * @return 'true' if user has any remote(!) role within any application (ECOMP Portal roles are not included), 'false' otherwise + */ + public boolean isUser(EPUser user); + + List getRolesByApp(EPUser user, Long appId); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AdminRolesServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AdminRolesServiceImpl.java new file mode 100644 index 00000000..50e7348e --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AdminRolesServiceImpl.java @@ -0,0 +1,329 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.apache.cxf.common.util.StringUtils; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPRole; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EPUserApp; +import org.openecomp.portalapp.portal.domain.UserIdRoleId; +import org.openecomp.portalapp.portal.domain.UserRole; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.transport.AppNameIdIsAdmin; +import org.openecomp.portalapp.portal.transport.AppsListWithAdminRole; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; + +@Service("adminRolesService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy + +public class AdminRolesServiceImpl implements AdminRolesService { + + private Long SYS_ADMIN_ROLE_ID = 1L; + private Long ACCOUNT_ADMIN_ROLE_ID = 999L; + private Long ECOMP_APP_ID = 1L; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AdminRolesServiceImpl.class); + + @Autowired + private SessionFactory sessionFactory; + @Autowired + private DataAccessService dataAccessService; + @Autowired + SearchService searchService; + @Autowired + EPAppService appsService; + + @PostConstruct + private void init() { + try { + SYS_ADMIN_ROLE_ID = Long.valueOf(SystemProperties.getProperty(EPCommonSystemProperties.SYS_ADMIN_ROLE_ID)); + ACCOUNT_ADMIN_ROLE_ID = Long.valueOf(SystemProperties.getProperty(EPCommonSystemProperties.ACCOUNT_ADMIN_ROLE_ID)); + ECOMP_APP_ID = Long.valueOf(SystemProperties.getProperty(EPCommonSystemProperties.ECOMP_APP_ID)); + } catch(Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } + } + + @Override + @EPMetricsLog + @SuppressWarnings("unchecked") + public AppsListWithAdminRole getAppsWithAdminRoleStateForUser(String orgUserId) { + AppsListWithAdminRole appsListWithAdminRole = null; + + try { + List userList = dataAccessService.getList(EPUser.class, " where orgUserId = '" + orgUserId + "'", null, + null); + HashMap appsUserAdmin = new HashMap(); + if (userList.size() > 0) { + EPUser user = userList.get(0); + List userAppList = null; + try { + userAppList = dataAccessService.getList(EPUserApp.class, + " where userId = " + user.getId() + " and role.id = " + ACCOUNT_ADMIN_ROLE_ID, null, null); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + EPLogUtil.logEcompError(EPAppMessagesEnum.BeDaoSystemError); + } + for (EPUserApp userApp : userAppList) { + appsUserAdmin.put(userApp.getAppId(), userApp.getUserId()); + } + } + + appsListWithAdminRole = new AppsListWithAdminRole(); + appsListWithAdminRole.orgUserId = orgUserId; + List appsList = null; + try { + appsList = dataAccessService.getList(EPApp.class, " where ( enabled = 'Y' or id = " + ECOMP_APP_ID + ")", null, null); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + EPLogUtil.logEcompError(EPAppMessagesEnum.BeDaoSystemError); + } + for (EPApp app : appsList) { + AppNameIdIsAdmin appNameIdIsAdmin = new AppNameIdIsAdmin(); + appNameIdIsAdmin.id = app.getId(); + appNameIdIsAdmin.appName = app.getName(); + appNameIdIsAdmin.isAdmin = new Boolean(appsUserAdmin.containsKey(app.getId())); + appNameIdIsAdmin.restrictedApp = app.isRestrictedApp(); + appsListWithAdminRole.appsRoles.add(appNameIdIsAdmin); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while performing AdminRolesServiceImpl.getAppsWithAdminRoleStateForUser operation, Details:" + + EcompPortalUtils.getStackTrace(e)); + } + + return appsListWithAdminRole; + } + + private static final Object syncRests = new Object(); + + @Override + @EPMetricsLog + @SuppressWarnings("unchecked") + public boolean setAppsWithAdminRoleStateForUser(AppsListWithAdminRole newAppsListWithAdminRoles) { + boolean result = false; + // No changes if no new roles list or no userId. + if (!StringUtils.isEmpty(newAppsListWithAdminRoles.orgUserId) && newAppsListWithAdminRoles.appsRoles != null) { + synchronized (syncRests) { + List apps = appsService.getAppsFullList(); + HashMap enabledApps = new HashMap(); + for (EPApp app : apps) { + if (app.getEnabled().booleanValue() || app.getId() == ECOMP_APP_ID) { + enabledApps.put(app.getId(), app); + } + } + List newAppsWhereUserIsAdmin = new ArrayList(); + for (AppNameIdIsAdmin adminRole : newAppsListWithAdminRoles.appsRoles) { + // user Admin role may be added only for enabled apps + if (adminRole.isAdmin.booleanValue() && enabledApps.containsKey(adminRole.id)) { + newAppsWhereUserIsAdmin.add(adminRole); + } + } + EPUser user = null; + boolean createNewUser = false; + String orgUserId = newAppsListWithAdminRoles.orgUserId.trim(); + List localUserList = dataAccessService.getList(EPUser.class, " where org_user_id='" + orgUserId + "'", + null, null); + List oldAppsWhereUserIsAdmin = new ArrayList(); + if (localUserList.size() > 0) { + EPUser tmpUser = localUserList.get(0); + oldAppsWhereUserIsAdmin = dataAccessService.getList(EPUserApp.class, + " where userId = " + tmpUser.getId() + " and role.id = " + ACCOUNT_ADMIN_ROLE_ID, null, + null); + if (oldAppsWhereUserIsAdmin.size() > 0 || newAppsWhereUserIsAdmin.size() > 0) { + user = tmpUser; + } + } else if (newAppsWhereUserIsAdmin.size() > 0) { + // we create new user only if he has Admin Role for any App + createNewUser = true; + } + if (user != null || createNewUser) { + Session localSession = null; + Transaction transaction = null; + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + if (createNewUser) { + user = this.searchService.searchUserByUserId(orgUserId); + if (user != null) { + // insert the user with active true in order to + // pass login phase. + user.setActive(true); + localSession.save(EPUser.class.getName(), user); + } + } + for (EPUserApp oldUserApp : oldAppsWhereUserIsAdmin) { + // user Admin role may be deleted only for enabled + // apps + if (enabledApps.containsKey(oldUserApp.getAppId())) { + localSession.delete(oldUserApp); + } + } + for (AppNameIdIsAdmin appNameIdIsAdmin : newAppsWhereUserIsAdmin) { + EPApp app = (EPApp) localSession.get(EPApp.class, appNameIdIsAdmin.id); + EPRole role = (EPRole) localSession.get(EPRole.class, new Long(ACCOUNT_ADMIN_ROLE_ID)); + EPUserApp newUserApp = new EPUserApp(); + newUserApp.setUserId(user.getId()); + newUserApp.setApp(app); + newUserApp.setRole(role); + localSession.save(EPUserApp.class.getName(), newUserApp); + } + transaction.commit(); + result = true; + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + logger.error(EELFLoggerDelegate.errorLogger, "setAppsWithAdminRoleStateForUser: exception in point 2", e); + try { + transaction.rollback(); + } catch (Exception ex) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeExecuteRollbackError, e); + logger.error(EELFLoggerDelegate.errorLogger, "setAppsWithAdminRoleStateForUser: exception in point 3", ex); + } + } finally { + try { + localSession.close(); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoCloseSessionError, e); + logger.error(EELFLoggerDelegate.errorLogger, "setAppsWithAdminRoleStateForUser: exception in point 4", e); + } + } + } + } + } + + return result; + } + + @SuppressWarnings("unchecked") + @Override + public boolean isSuperAdmin(EPUser user) { + if ((user != null) /* && (user.getId() == null) */ && (user.getOrgUserId() != null)) { + String sql = "SELECT user.USER_ID, user.org_user_id, userrole.ROLE_ID, userrole.APP_ID FROM fn_user_role userrole " + + "INNER JOIN fn_user user ON user.USER_ID = userrole.USER_ID " + "WHERE user.org_user_id = '" + + user.getOrgUserId() + "' " + "AND userrole.ROLE_ID = '" + SYS_ADMIN_ROLE_ID + "' " + + "AND userrole.APP_ID = '" + ECOMP_APP_ID + "';"; + try { + List userRoleList = dataAccessService.executeSQLQuery(sql, UserIdRoleId.class, null); + if (userRoleList != null && userRoleList.size() > 0) { + return true; + } + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while executing isSuperAdmin operation", e); + } + } + // else + // { + // User currentUser = user != null ? (User) + // dataAccessService.getDomainObject(User.class, user.getId(), null) : + // null; + // if (currentUser != null && currentUser.getId() != null) { + // for (UserApp userApp : currentUser.getUserApps()) { + // if (userApp.getApp().getId().equals(ECOMP_APP_ID) && + // userApp.getRole().getId().equals(SYS_ADMIN_ROLE_ID)) { + // // Super Administrator role is global, no need to keep iterating + // return true; + // } + // } + // } + // } + return false; + } + + public boolean isAccountAdmin(EPUser user) { + try { + EPUser currentUser = user != null + ? (EPUser) dataAccessService.getDomainObject(EPUser.class, user.getId(), null) : null; + if (currentUser != null && currentUser.getId() != null) { + for (EPUserApp userApp : currentUser.getEPUserApps()) { + if (//!userApp.getApp().getId().equals(ECOMP_APP_ID) + // && + userApp.getRole().getId().equals(ACCOUNT_ADMIN_ROLE_ID)) { + // Account Administrator sees only the applications + // he/she is Administrator + return true; + } + } + } + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while executing isAccountAdmin operation", e); + } + return false; + } + + public boolean isUser(EPUser user) { + try { + EPUser currentUser = user != null + ? (EPUser) dataAccessService.getDomainObject(EPUser.class, user.getId(), null) : null; + if (currentUser != null && currentUser.getId() != null) { + for (EPUserApp userApp : currentUser.getEPUserApps()) { + if (!userApp.getApp().getId().equals(ECOMP_APP_ID)) { + EPRole role = userApp.getRole(); + if (!role.getId().equals(SYS_ADMIN_ROLE_ID) && !role.getId().equals(ACCOUNT_ADMIN_ROLE_ID)) { + if (role.getActive()) { + return true; + } + } + } + } + } + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while executing isUser operation", e); + } + return false; + } + + @Override + @EPMetricsLog + public List getRolesByApp(EPUser user, Long appId) { + List list = new ArrayList<>(); + String sql = "SELECT * FROM FN_ROLE WHERE APP_ID = " + appId; + @SuppressWarnings("unchecked") + List roles = dataAccessService.executeSQLQuery(sql, EPRole.class, null); + for (EPRole role: roles) { + list.add(role); + } + return list; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppContactUsService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppContactUsService.java new file mode 100644 index 00000000..e8ef64fd --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppContactUsService.java @@ -0,0 +1,55 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.ecomp.model.AppCategoryFunctionsItem; +import org.openecomp.portalapp.portal.ecomp.model.AppContactUsItem; + +public interface AppContactUsService { + + /** + * Gets a list of contact-us information for all entries in + * the fn_app_contact_us table, sorted by app name. If an application is active but has no fn_app_contact_us entry, it will have no entry in this result. + * + * @return List of AppContactUsItem, one for each item in fn_app_contact_us table. + * @throws Exception + */ + public List getAppContactUs() throws Exception; + + /** + * Gets a list of contact-us information for all applications + * in the fn_app table, extended with any information in the fn_app_contact_us table. + * + * @return List of AppContactUsItem, one for each item in fn_app table. + * @throws Exception + */ + public List getAppsAndContacts() throws Exception; + + public List getAppCategoryFunctions() throws Exception; + + public String saveAppContactUs(List contactUs) throws Exception; + + public String saveAppContactUs(AppContactUsItem contactUs) throws Exception; + + public String deleteContactUs(Long id) throws Exception; + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppContactUsServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppContactUsServiceImpl.java new file mode 100644 index 00000000..41c5d9fd --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppContactUsServiceImpl.java @@ -0,0 +1,189 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalapp.portal.domain.AppContactUs; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.ecomp.model.AppCategoryFunctionsItem; +import org.openecomp.portalapp.portal.ecomp.model.AppContactUsItem; + +/** + * Provides database access for the contact-us page controllers. + */ +@Transactional +@org.springframework.context.annotation.Configuration +public class AppContactUsServiceImpl implements AppContactUsService { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AppContactUsServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + + public void setDataAccessService(DataAccessService dataAccessService) { + this.dataAccessService = dataAccessService; + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalapp.portal.service.AppContactUsService# + * getAppContactUs() + */ + @SuppressWarnings("unchecked") + @Override + public List getAppContactUs() throws Exception { + List contactUsItemList = (List) getDataAccessService() + .executeNamedQuery("getAppContactUsItems", null, null); + Collections.sort(contactUsItemList, new AppContactUsItemCompare()); + return contactUsItemList; + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalapp.portal.service.AppContactUsService# + * getAllAppsAndContacts() + */ + @SuppressWarnings("unchecked") + @Override + public List getAppsAndContacts() throws Exception { + List contactUsItemList = (List) getDataAccessService() + .executeNamedQuery("getAppsAndContacts", null, null); + Collections.sort(contactUsItemList, new AppContactUsItemCompare()); + return contactUsItemList; + } + + /** + * Assists in sorting app-contact-us items by application name. + */ + class AppContactUsItemCompare implements Comparator { + @Override + public int compare(AppContactUsItem o1, AppContactUsItem o2) { + return o1.getAppName().compareTo(o2.getAppName()); + } + } + + /** + * Gets a table of category and function details for apps that participate + * in the functional menu. + */ + @Override + public List getAppCategoryFunctions() throws Exception { + @SuppressWarnings("unchecked") + // This named query requires no parameters. + List list = (List) dataAccessService + .executeNamedQuery("getAppCategoryFunctions", null, null); + logger.debug(EELFLoggerDelegate.debugLogger, "getAppCategoryFunctions: result list size is " + list.size()); + return list; + } + + /** + * Saves the list of contact-us objects to the database. + */ + @Override + @Transactional(rollbackFor = Exception.class) + public String saveAppContactUs(List contactUsModelList) throws Exception { + try { + for (AppContactUsItem contactUs : contactUsModelList) { + String status = saveAppContactUs(contactUs); + if (!status.equals("success")) + throw new Exception("saveAppContaatUsFailed: service returned " + status); + } + return "success"; + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "", e); + throw new Exception(e); + } + + } + + /** + * Saves a single contact-us object to the database, either creating a new + * row or updating if the argument has the ID of an existing row. + */ + @Override + @Transactional(rollbackFor = Exception.class) + public String saveAppContactUs(AppContactUsItem contactUsModel) throws Exception { + try { + HashMap map = new HashMap(); + AppContactUs contactUs = null; + try { + contactUs = (AppContactUs) getDataAccessService().getDomainObject(AppContactUs.class, + contactUsModel.getAppId(), map); + } catch (Exception e) { + logger.debug(EELFLoggerDelegate.debugLogger, "saveAppContactUs: not found for App {}, adding new entry", + contactUsModel.getAppName()); + contactUs = new AppContactUs(); + } + + // Populate the AppContactUs model for the database. + EPApp app = (EPApp) getDataAccessService().getDomainObject(EPApp.class, contactUsModel.getAppId(), map); + if (app == null || app.getId() == null) + throw new Exception("saveAppContactus: App not found for Id " + contactUsModel.getAppId()); + contactUs.setApp(app); + contactUs.setDescription(contactUsModel.getDescription()); + contactUs.setContactName(contactUsModel.getContactName()); + contactUs.setContactEmail(contactUsModel.getContactEmail()); + contactUs.setActiveYN(contactUsModel.getActiveYN()); + contactUs.setUrl(contactUsModel.getUrl()); + getDataAccessService().saveDomainObject(contactUs, map); + return "success"; + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "saveAppContactUs failed", e); + throw e; + // return "failure"; + } + } + + /** + * Deletes the row from the app contact us table with the specified ID. + */ + @Override + public String deleteContactUs(Long id) throws Exception { + try { + HashMap map = new HashMap(); + AppContactUs contactUs = (AppContactUs) getDataAccessService().getDomainObject(AppContactUs.class, id, map); + if (contactUs.getApp() == null) + throw new Exception("Delete unsuccessful for Id " + id); + getDataAccessService().deleteDomainObject(contactUs, map); + return "success"; + } catch (Exception e) { + + logger.info(EELFLoggerDelegate.errorLogger, "", e); + throw e; + } + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ApplicationsRestClientService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ApplicationsRestClientService.java new file mode 100644 index 00000000..caeceb3c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ApplicationsRestClientService.java @@ -0,0 +1,53 @@ +/*- + * ================================================================================ + * ECOMP Portal SDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import org.apache.cxf.transport.http.HTTPException; +import org.openecomp.portalapp.util.SystemType; + +public interface ApplicationsRestClientService { + public T get(Class clazz, long app, String restPath) throws HTTPException; + public String getIncomingJsonString(long appId, String restPath) throws HTTPException; + + public T post(Class clazz, long appId, Object payload, String restPath) throws HTTPException; + public T post(Class clazz, long appId, Object payload, String restPath, SystemType type) throws HTTPException; + + public T put(Class clazz, long appId, Object payload, String restPath) throws HTTPException; + + /** + * Sends a GET request to the specified application at the specified path. + * This is a workaround for a problem triggered by a superclass/subclass + * with identical field names. + * + * @param clazz + * Expected response type + * @param appId + * Application ID + * @param restPath + * Path at the remote application + * @param useJacksonMapper + * If true, uses a com.fasterxml.jackson.databind.ObjectMapper to + * translate the remote application response from JSON to an + * object. Otherwise, uses a com.google.gson.Gson. + * @return Instance of the specified class + * @throws HTTPException + */ + public T get(Class clazz, long appId, String restPath, boolean useJacksonMapper) throws HTTPException; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ApplicationsRestClientServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ApplicationsRestClientServiceImpl.java new file mode 100644 index 00000000..027d972c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ApplicationsRestClientServiceImpl.java @@ -0,0 +1,411 @@ +/*- + * ================================================================================ + * ECOMP Portal SDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID; + +import java.lang.reflect.Type; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Base64; +import java.util.Date; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.transport.http.HTTPException; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalapp.util.SystemType; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.util.CipherUtil; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +@Service("applicationsRestClientService") +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPAuditLog +public class ApplicationsRestClientServiceImpl implements ApplicationsRestClientService { + + private static final String PASSWORD_HEADER = "password"; + + private static final String APP_USERNAME_HEADER = "username"; + + private static final String BASIC_AUTHENTICATION_HEADER = "Authorization"; + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ApplicationsRestClientServiceImpl.class); + + @Autowired + private AppsCacheService appsCacheService; + + Gson gson = null; + + private final ObjectMapper mapper = new ObjectMapper(); + + @PostConstruct + private void init() { + logger.debug(EELFLoggerDelegate.debugLogger, "initializing"); + GsonBuilder builder = new GsonBuilder(); + + // Register an adapter to manage the date types as long values + builder.registerTypeAdapter(Date.class, new JsonDeserializer() { + public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return new Date(json.getAsJsonPrimitive().getAsLong()); + } + }); + + gson = builder.create(); + } + + // TODO: do we need to do additional logging for remote API calls? + private static WebClient createClientForPath(String baseUri, String path) { + logger.info(EELFLoggerDelegate.debugLogger, "Creating web client for " + baseUri + " + " + path); + WebClient client = WebClient.create(baseUri); + client.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON); + client.path(path); + return client; + } + + @EPMetricsLog + private void verifyResponse(Response response) throws HTTPException { + int status = response.getStatus(); + logger.debug(EELFLoggerDelegate.debugLogger, "http response status=" + status); + MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE, Integer.toString(status)); + if (!isHttpSuccess(status)) { + String errMsg = "Failed. Status=" + status + "; [" + response.getStatusInfo().getReasonPhrase().toString() + + "]"; + URL url = null; + try { + // must not be null to avoid NPE in HTTPException constructor + url = new URL("http://null"); + if (response.getLocation() != null) + url = response.getLocation().toURL(); + } catch (MalformedURLException e) { + // never mind. it is only for the debug message. + logger.warn(EELFLoggerDelegate.errorLogger, "Failed to build URL", e); + } + logger.error(EELFLoggerDelegate.errorLogger, "http response failed. " + errMsg + "; url=" + url); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeIncorrectHttpStatusError); + throw new HTTPException(status, errMsg, url); + } + } + + private static boolean isHttpSuccess(int status) { + return status / 100 == 2; + } + + + private WebClient createClientForApp(long appId, String restPath) { + return createClientFor(appId, restPath, SystemType.APPLICATION); + } + + //TODO Need to implement the mylogins once the endpoint is confirmed + @EPMetricsLog + private WebClient createClientFor(long appSystemId, String restPath, SystemType type) { + logger.debug(EELFLoggerDelegate.debugLogger, "creating client for appId=" + appSystemId + "; restPath=" + restPath); + EPApp externalApp = null; + + if(type == SystemType.APPLICATION){ + externalApp = appsCacheService.getApp(appSystemId); + }else{ + // TO DO + } + + if (externalApp != null) { + String appBaseUri = (type == SystemType.APPLICATION) ? externalApp.getAppRestEndpoint() : ""; + String username = (type == SystemType.APPLICATION) ? externalApp.getUsername(): ""; + String encriptedPwd = (type == SystemType.APPLICATION) ? externalApp.getAppPassword(): ""; + String appName = (type == SystemType.APPLICATION) ? externalApp.getName(): ""; + String decreptedAppPwd = ""; + + // Set local context + MDC.put(EPCommonSystemProperties.PROTOCOL, EPCommonSystemProperties.HTTP); + if (appBaseUri != null && appBaseUri.contains("https")) { + MDC.put(EPCommonSystemProperties.PROTOCOL, EPCommonSystemProperties.HTTPS); + } + MDC.put(EPCommonSystemProperties.FULL_URL, appBaseUri + restPath); + MDC.put(EPCommonSystemProperties.TARGET_ENTITY, appName); + MDC.put(EPCommonSystemProperties.TARGET_SERVICE_NAME, restPath); + + try { + decreptedAppPwd = CipherUtil.decrypt(encriptedPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Unable to decrypt App name = " + externalApp, + EcompPortalUtils.getStackTrace(e)); + logger.error(EELFLoggerDelegate.errorLogger, "Unable to decrypt App name = " + externalApp, + EcompPortalUtils.getStackTrace(e)); + } + + WebClient client = createClientForPath(appBaseUri, restPath); + + // support basic authentication for some partners + String encoding = Base64.getEncoder().encodeToString((username + ":" + decreptedAppPwd).getBytes()); + String encodingStr = "Basic " + encoding; + client.header(BASIC_AUTHENTICATION_HEADER, encodingStr); + + // But still keep code downward compatible for non compliant apps + client.header(APP_USERNAME_HEADER, username); + client.header(PASSWORD_HEADER, decreptedAppPwd); + + client.header(SystemProperties.ECOMP_REQUEST_ID, MDC.get(MDC_KEY_REQUEST_ID)); + client.header(SystemProperties.USERAGENT_NAME, EPCommonSystemProperties.ECOMP_PORTAL_BE); + + logger.debug(EELFLoggerDelegate.debugLogger, + String.format("App %d found, baseUri=[%s], Headers: [%s=%s, %s=%s, %s=%s]", appSystemId, appBaseUri, + APP_USERNAME_HEADER, username, PASSWORD_HEADER, encriptedPwd, BASIC_AUTHENTICATION_HEADER, encodingStr)); + + return client; + } + return null; + } + + @Override + public T get(Class clazz, long appId, String restPath) throws HTTPException { + T t = null; + Response response = getResponse(appId, restPath); + + if (response != null) { + verifyResponse(response); + String str = response.readEntity(String.class); + EcompPortalUtils.logAndSerializeObject(logger, restPath, "GET result =", str); + try { + t = gson.fromJson(str, clazz); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e); + } + } + + return t; + } + + @Override + public String getIncomingJsonString(long appId, String restPath) throws HTTPException { + Response response = getResponse(appId, restPath); + + if (response != null) { + verifyResponse(response); + String incomingJson = response.readEntity(String.class); + return incomingJson; + } + + return ""; + } + + + /* + * (non-Javadoc) + * + * @see + * org.openecomp.portalapp.portal.service.ApplicationsRestClientService#get( + * java.lang.Class, long, java.lang.String, boolean) + */ + @Override + public T get(Class clazz, long appId, String restPath, boolean useJacksonMapper) throws HTTPException { + + if (!useJacksonMapper) + return get(clazz, appId, restPath); + + T t = null; + Response response = getResponse(appId, restPath); + + if (response != null) { + verifyResponse(response); + String str = response.readEntity(String.class); + EcompPortalUtils.logAndSerializeObject(logger, restPath, "GET result =", str); + + try { + t = mapper.readValue(str, clazz); + } catch (Exception e) { + e.printStackTrace(); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e); + } + } + + return t; + } + + protected Response getResponse(long appId, String restPath) { + WebClient webClient = null; + Response response = null; + + webClient = createClientForApp(appId, restPath); + EcompPortalUtils.logAndSerializeObject(logger, restPath, "GET request =", "no-payload"); + + try { + if (webClient != null) { + response = webClient.get(); + } else { + logger.error(EELFLoggerDelegate.errorLogger, + "Unable to create the Webclient to make the '" + restPath + "' API call."); + } + } catch (Exception e) { + MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE, + Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiGeneralError, e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while making the GET REST API call", e); + } + return response; + } + + + @Override + public T post(Class clazz, long appId, Object payload, String restPath, SystemType type) throws HTTPException { + WebClient client = null; + Response response = null; + T t = null; + + client = createClientFor(appId, restPath, type); + EcompPortalUtils.logAndSerializeObject(logger, restPath, "POST request =", payload); + + try { + if (client != null) { + response = client.post(payload); + } else { + logger.error(EELFLoggerDelegate.errorLogger, + "Unable to create the Webclient to make the '" + restPath + "' API call."); + } + } catch (Exception e) { + MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE, + Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiGeneralError, e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while making the POST REST API call", e); + } + + if (response != null) { + verifyResponse(response); + + // String contentType = response.getHeaderString("Content-Type"); + if (clazz != null) { + String str = response.readEntity(String.class); + EcompPortalUtils.logAndSerializeObject(logger, restPath, "POST result =", str); + try { + t = gson.fromJson(str, clazz); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e); + } + } + } + return t; + } + + @Override + public T post(Class clazz, long appId, Object payload, String restPath) throws HTTPException { + return post( clazz, appId, payload, restPath, SystemType.APPLICATION); + } + + //@Override + public T postForClass(Class clazz, long appId, Object payload, String restPath, Class forClass) throws HTTPException { + WebClient client = null; + Response response = null; + T t = null; + + client = createClientForApp(appId, restPath); + EcompPortalUtils.logAndSerializeObject(logger, restPath, "POST request =", payload); + + try { + if (client != null) { + response = client.post(payload); + } else { + logger.error(EELFLoggerDelegate.errorLogger, + "Unable to create the Webclient to make the '" + restPath + "' API call."); + } + } catch (Exception e) { + MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE, + Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiGeneralError, e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while making the POST REST API call", e); + } + + if (response != null) { + verifyResponse(response); + + // String contentType = response.getHeaderString("Content-Type"); + if (clazz != null) { + String str = response.readEntity(String.class); + EcompPortalUtils.logAndSerializeObject(logger, restPath, "POST result =", str); + try { + t = gson.fromJson(str, clazz); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e); + } + } + } + return t; + } + + + @Override + public T put(Class clazz, long appId, Object payload, String restPath) throws HTTPException { + WebClient client = null; + Response response = null; + T t = null; + + client = createClientForApp(appId, restPath); + EcompPortalUtils.logAndSerializeObject(logger, restPath, "PUT request =", payload); + + try { + if (client != null) { + response = client.put(payload); + } else { + logger.error(EELFLoggerDelegate.errorLogger, + "Unable to create the Webclient to make the '" + restPath + "' API call."); + } + } catch (Exception e) { + MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE, + Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiGeneralError, e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while making the PUT REST API call", e); + } + + if (response != null) { + verifyResponse(response); + String str = response.readEntity(String.class); + EcompPortalUtils.logAndSerializeObject(logger, restPath, "PUT result =", str); + try { + t = gson.fromJson(str, clazz); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e); + } + } + return t; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppsCacheService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppsCacheService.java new file mode 100644 index 00000000..aefbbbd5 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppsCacheService.java @@ -0,0 +1,42 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +package org.openecomp.portalapp.portal.service; + +import org.openecomp.portalapp.portal.domain.EPApp; + +public interface AppsCacheService { + + /** + * returns an app by id from the cache + * @param appId + * @return corresponding App + */ + EPApp getApp(Long appId); + + /** + * returns the corresponding application endpoint + * @param appId + * @return if appId exists in cache, then return corresponding application endpoint, null otherwise. + */ + String getAppEndpoint(Long appId); + + EPApp getAppForAnalytics(String appKey); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppsCacheServiceImple.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppsCacheServiceImple.java new file mode 100644 index 00000000..80aa00d4 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/AppsCacheServiceImple.java @@ -0,0 +1,136 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; + +@Service("appsCacheService") +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class AppsCacheServiceImple implements AppsCacheService { + @Autowired + EPAppService appsService; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AppsCacheServiceImple.class); + + final class CacheConfiguration { + + private long updateTime = 0; + private int updateInterval = 10; + + public CacheConfiguration (long _updateTime, int _updateInterval) { + updateTime = _updateTime; + updateInterval = _updateInterval; + } + } + + CacheConfiguration appConf = null; + CacheConfiguration analyticsAppConf = null; + + + private static volatile Map appsMap; + private static volatile Map anlyticsAppsMap; + + @PostConstruct + public void init() { + appConf = new CacheConfiguration(0, 10); + analyticsAppConf = new CacheConfiguration(0, 3600); + + this.refreshAppsMap(appConf); + } + + private Map refreshAppsMap(CacheConfiguration conf) { + long now = System.currentTimeMillis(); + + if(noNeedToUpdate(now, conf)) + return null; + + synchronized (this) { + if(noNeedToUpdate(now, conf)) + return null; + List allApps = appsService.getAppsFullList(); + Map newAppsMap = new HashMap(); + for (EPApp app : allApps) { + newAppsMap.put(app.getId(), app); + } + + Map newAnalyticsAppsMap = new HashMap(); + for (EPApp app : allApps) { + newAnalyticsAppsMap.put(app.getUebKey(), app); + } + // Switch cache with the new one. + appsMap = newAppsMap; + anlyticsAppsMap = newAnalyticsAppsMap; + conf.updateTime = now; + } + + return appsMap; + } + + private boolean noNeedToUpdate(long now, CacheConfiguration conf) { + long secondsPassed = (now - conf.updateTime)/1000; + if(secondsPassed < conf.updateInterval){ + logger.debug(EELFLoggerDelegate.debugLogger, "no need to refresh yet, seconds since last refresh: " + secondsPassed + ", refresh interval (sec) = " + conf.updateInterval); + return true; // no need to update cache + } + return false; // its time to update + } + + @Override + public String getAppEndpoint(Long appId) { + refreshAppsMap(appConf); + EPApp app = appsMap.get(appId); + if(app != null) + return app.getAppRestEndpoint(); + return null; + } + + @Override + public EPApp getApp(Long appId) { + refreshAppsMap(appConf); + EPApp app = appsMap.get(appId); + if(app != null) + return app; + return null; + } + + @Override + public EPApp getAppForAnalytics(String appKey) { + refreshAppsMap(analyticsAppConf); + EPApp app = anlyticsAppsMap.get(appKey); + if(app != null) + return app; + return null; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthAccountService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthAccountService.java new file mode 100644 index 00000000..011a6485 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthAccountService.java @@ -0,0 +1,47 @@ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.BasicAuthCredentials; +import org.openecomp.portalapp.portal.domain.EPEndpoint; + +public interface BasicAuthAccountService { + + /** + * Saves Basic Authentication account for external systems + * @param BasicAuthCredentials + * @return Id of the newly created account + */ + Long saveBasicAuthAccount(BasicAuthCredentials newCredential) throws Exception; + + /** + * Saves Endpoint associated with a Basic Auth account + * @param EPEndpoint + * @return Id of the newly created endpoint + */ + Long saveEndpoints(EPEndpoint endpoint) throws Exception; + + /** + * Saves Endpoint associated with a Basic Auth account + * @param accountId, endpointId + */ + void saveEndpointAccount(Long accountId, Long endpointId) throws Exception; + + /** + * Returns list of all BasicAuthCredentials in the sytem + * @return List + */ + List getAccountData() throws Exception; + + /** + * Deletes BasicAuthenticationAccount + * @param accountId + */ + void deleteEndpointAccout(Long accountId) throws Exception; + + /** + * Updates BasicAuthenticationAccount + * @param accountId, BasicAuthCredentials + */ + void updateBasicAuthAccount(Long accountId, BasicAuthCredentials newCredential) throws Exception; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthAccountServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthAccountServiceImpl.java new file mode 100644 index 00000000..fd0fb597 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthAccountServiceImpl.java @@ -0,0 +1,205 @@ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hibernate.Session; +import org.hibernate.criterion.Criterion; +import org.hibernate.criterion.Restrictions; +import org.openecomp.portalapp.portal.domain.BasicAuthCredentials; +import org.openecomp.portalapp.portal.domain.EPEndpoint; +import org.openecomp.portalapp.portal.domain.EPEndpointAccount; +import org.openecomp.portalapp.portal.domain.MicroserviceData; +import org.openecomp.portalapp.portal.domain.MicroserviceParameter; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.util.CipherUtil; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +@Service("basicAuthAccountService") +@EnableAspectJAutoProxy +@EPMetricsLog +public class BasicAuthAccountServiceImpl implements BasicAuthAccountService{ + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MicroserviceServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + + @Override + public Long saveBasicAuthAccount(BasicAuthCredentials newCredential) throws Exception { + if (newCredential.getPassword() != null) + newCredential.setPassword(encryptedPassword(newCredential.getPassword())); + try{ + getDataAccessService().saveDomainObject(newCredential, null); + }catch(Exception e){ + logger.error(EELFLoggerDelegate.errorLogger, "saveBasicAuthAccount() failed", e); + throw e; + } + return newCredential.getId(); + } + + + @Override + @SuppressWarnings("unchecked") + public Long saveEndpoints(EPEndpoint endpoint) throws Exception { + + List restrictionsList = new ArrayList(); + Criterion NameCrit = Restrictions.eq("name", endpoint.getName()); + restrictionsList.add(NameCrit); + + List tempList = (List) dataAccessService.getList(EPEndpoint.class, null, + restrictionsList, null); + if (tempList.size() != 0) { + return tempList.get(0).getId(); + } else { + getDataAccessService().saveDomainObject(endpoint, null); + return endpoint.getId(); + } + + } + + @Override + public void saveEndpointAccount(Long accountId, Long endpointId) throws Exception { + EPEndpointAccount record = new EPEndpointAccount(); + record.setAccount_id(accountId); + record.setEp_id(endpointId); + try { + getDataAccessService().saveDomainObject(record, null); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "saveEndpointAccount() failed", e); + throw e; + } + + } + + @Override + @SuppressWarnings("unchecked") + public void updateBasicAuthAccount(Long accountId, BasicAuthCredentials newCredential) throws Exception { + try { + newCredential.setId(accountId); + if (newCredential.getPassword() != null) + newCredential.setPassword(encryptedPassword(newCredential.getPassword())); + getDataAccessService().saveDomainObject(newCredential, null); + + List endpoints = newCredential.getEndpoints(); + List orig_points = getEPEndpoints(accountId); + + for(EPEndpoint temp_ep: orig_points){ + boolean flag = false; + for(EPEndpoint temp_ep2: endpoints){ + if(temp_ep2.getId() == temp_ep.getId()) + flag = true; + } + if(!flag){ + Map params = new HashMap(); + params.put("accountId", Long.toString(accountId)); + params.put("epId", Long.toString(temp_ep.getId())); + dataAccessService.executeNamedQuery("deleteAccountEndpointRecord", params, null); + } + } + + + for(int i = 0; i < endpoints.size(); i++){ + + List restrictionsList = new ArrayList(); + Criterion IdCrit = Restrictions.eq("id", endpoints.get(i).getId()); + restrictionsList.add(IdCrit); + Criterion NameCrit = Restrictions.eq("name", endpoints.get(i).getName()); + restrictionsList.add(NameCrit); + List tempList = (List) dataAccessService + .getList(EPEndpoint.class, null, restrictionsList, null); + if(tempList.size() == 0){ + if(endpoints.get(i).getId() != null){ + //delete the record endpoints.get(i).getId(), accountId + Map params = new HashMap(); + params.put("accountId", Long.toString(accountId)); + params.put("epId", Long.toString(endpoints.get(i).getId())); + dataAccessService.executeNamedQuery("deleteAccountEndpointRecord", params, null); + endpoints.get(i).setId(null); + } + //create a new endpoint + Long ep_id = saveEndpoints(endpoints.get(i)); + saveEndpointAccount(accountId, ep_id); + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "updateBasicAuthAccount() failed", e); + throw e; + } + } + + @Override + public List getAccountData() throws Exception { + List list = (List) dataAccessService.getList(BasicAuthCredentials.class, null); + for (int i = 0; i < list.size(); i++) { + if (list.get(i).getPassword() != null) + list.get(i).setPassword(decryptedPassword(list.get(i).getPassword())); + list.get(i).setEndpoints(getEPEndpoints(list.get(i).getId())); + } + return list; + } + + @SuppressWarnings("unchecked") + private List getEPEndpoints(long accountId) { + List result = new ArrayList<>(); + List list = (List) dataAccessService + .getList(EPEndpointAccount.class, " where account_id = '" + accountId + "'", null, null); + for(int i = 0; i < list.size(); i++){ + result.add((EPEndpoint) dataAccessService.getDomainObject(EPEndpoint.class, list.get(i).getEp_id(), null)); + } + return result; + } + + @Override + public void deleteEndpointAccout(Long accountId) throws Exception { + try{ + Map params = new HashMap(); + params.put("accountId", Long.toString(accountId)); + + dataAccessService.executeNamedQuery("deleteAccountEndpoint", params, null); + dataAccessService.executeNamedQuery("deleteBasicAuthAccount", params, null); + + }catch(Exception e){ + logger.error(EELFLoggerDelegate.errorLogger, "deleteEndpointAccout() failed", e); + throw e; + } + } + + private String decryptedPassword(String encryptedPwd) throws Exception { + String result = ""; + if (encryptedPwd != null & encryptedPwd.length() > 0) { + try { + result = CipherUtil.decrypt(encryptedPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "decryptedPassword() failed", e); + throw e; + } + } + return result; + } + + private String encryptedPassword(String decryptedPwd) throws Exception { + String result = ""; + if (decryptedPwd != null & decryptedPwd.length() > 0) { + try { + result = CipherUtil.encrypt(decryptedPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "encryptedPassword() failed", e); + throw e; + } + } + return result; + } + + public DataAccessService getDataAccessService() { + return dataAccessService; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthenticationCredentialService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthenticationCredentialService.java new file mode 100644 index 00000000..459a6504 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthenticationCredentialService.java @@ -0,0 +1,35 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import org.openecomp.portalapp.portal.domain.BasicAuthCredentials; + +public interface BasicAuthenticationCredentialService { + /** + * Gets the basic authentication credential for the specified appName from + * table ep_basic_auth_account + * + * @param appName + * External application name + * @return BasicAuthCredentials for the specified app; null if the app is + * not known. + */ + public BasicAuthCredentials getBasicAuthCredentialByAppName(String appName); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthenticationCredentialServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthenticationCredentialServiceImpl.java new file mode 100644 index 00000000..54a9d2af --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/BasicAuthenticationCredentialServiceImpl.java @@ -0,0 +1,81 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.criterion.Criterion; +import org.hibernate.criterion.Restrictions; +import org.openecomp.portalapp.portal.domain.BasicAuthCredentials; +import org.openecomp.portalapp.portal.domain.EPEndpoint; +import org.openecomp.portalapp.portal.domain.EPEndpointAccount; +import org.openecomp.portalapp.portal.domain.SharedContext; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +@Service("basicAuthenticationCredentialService") +@EnableAspectJAutoProxy +@EPMetricsLog +public class BasicAuthenticationCredentialServiceImpl implements BasicAuthenticationCredentialService { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(BasicAuthenticationCredentialServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + + @Override + public BasicAuthCredentials getBasicAuthCredentialByAppName(String appName) { + + List restrictionsList = new ArrayList(); + Criterion contextIdCrit = Restrictions.eq("applicationName", appName); + restrictionsList.add(contextIdCrit); + @SuppressWarnings("unchecked") + List credList = (List) dataAccessService.getList(BasicAuthCredentials.class, null, restrictionsList, null); + if (credList == null || credList.size() == 0) { + logger.error(EELFLoggerDelegate.errorLogger, + "getBasicAuthCredentialByAppName: no credential(s) for " + appName); + return null; + } + logger.debug(EELFLoggerDelegate.debugLogger, + "getBasicAuthCredentialByAppName: cred list size: " + credList.size()); + BasicAuthCredentials cred = (BasicAuthCredentials) credList.get(0); + cred.setEndpoints(getEndpointsByAccountId(cred.getId())); + return cred; + } + + private List getEndpointsByAccountId(long id){ + + List list = new ArrayList<>(); + List restrictionsList = new ArrayList(); + Criterion contextIdCrit = Restrictions.eq("account_id", id); + restrictionsList.add(contextIdCrit); + List epList = (List) dataAccessService.getList(EPEndpointAccount.class, null, restrictionsList, null); + for(EPEndpointAccount ep: epList){ + list.add((EPEndpoint) dataAccessService.getDomainObject(EPEndpoint.class, ep.getEp_id(), null)); + } + return list; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ConsulHealthService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ConsulHealthService.java new file mode 100644 index 00000000..6ceb2cac --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ConsulHealthService.java @@ -0,0 +1,32 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.exceptions.NoHealthyServiceException; +import com.ecwid.consul.ConsulException; +import com.orbitz.consul.model.health.ServiceHealth; + +public interface ConsulHealthService { + public String getServiceLocation(String service) throws NoHealthyServiceException; + public List getAllHealthyNodes(String service) throws ConsulException; + public List getAllNodes(String service) throws ConsulException; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ConsulHealthServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ConsulHealthServiceImpl.java new file mode 100644 index 00000000..1d741dca --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ConsulHealthServiceImpl.java @@ -0,0 +1,91 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.springframework.stereotype.Component; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import com.ecwid.consul.ConsulException; +import com.orbitz.consul.Consul; +import com.orbitz.consul.HealthClient; +import com.orbitz.consul.model.health.ServiceHealth; + +@Component +public class ConsulHealthServiceImpl implements ConsulHealthService { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ConsulHealthServiceImpl.class); + + @Override + public String getServiceLocation(String service){ + + List nodes = null; + + try{ + Consul consul = Consul.builder().build(); + HealthClient healthClient = consul.healthClient(); + nodes = healthClient.getHealthyServiceInstances(service).getResponse(); + } + catch(Exception e){ + //using both loggers. + logger.debug(logger.debugLogger, " problem getting nodes for service - " + service + e.getMessage() + " - Defaulting to localhost", e); + logger.error(logger.errorLogger, " problem getting nodes for service - " + service + e.getMessage() + " - Defaulting to localhost", e); + + return "localhost:" + SystemProperties.getProperty("microservices.widget.local.port"); + } + + if(nodes == null || nodes.size() == 0){ + logger.debug(logger.debugLogger, "No healthy node found in the consul cluster running service " + service + ". Defaulting to localhost"); + return "localhost:" + SystemProperties.getProperty("microservices.widget.local.port"); + } + else{ + String locationFromConsul; + ServiceHealth node = nodes.get(0); + locationFromConsul = node.getNode().getNode() + ":" + node.getService().getPort(); + logger.debug(logger.debugLogger, "Found healthy service location using consul - returning location " + locationFromConsul); + + //if locationFromConsul is null for some reason (very unlikely at this point), default to localhost + if(null == locationFromConsul || "".equals(locationFromConsul)){ + logger.debug(logger.debugLogger, "Couldn't get location from consul for service " + service + ". Defaulting to localhost"); + return "localhost:" + SystemProperties.getProperty("microservices.widget.local.port"); + } + else{ + logger.debug(logger.debugLogger, "Found service location from consul for service " + service + ". Location is " + locationFromConsul); + return locationFromConsul; + } + } + } + + @Override + public List getAllHealthyNodes(String service) throws ConsulException{ + Consul consul = Consul.builder().build(); + HealthClient healthClient = consul.healthClient(); + return healthClient.getHealthyServiceInstances(service).getResponse(); + } + + @Override + public List getAllNodes(String service){ + Consul consul = Consul.builder().build(); + HealthClient healthClient = consul.healthClient(); + return healthClient.getAllServiceInstances(service).getResponse(); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/DashboardSearchService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/DashboardSearchService.java new file mode 100644 index 00000000..177f0e6a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/DashboardSearchService.java @@ -0,0 +1,37 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; +import java.util.Map; + +import org.openecomp.portalapp.portal.transport.CommonWidget; +import org.openecomp.portalapp.portal.transport.CommonWidgetMeta; +import org.openecomp.portalapp.portal.ecomp.model.SearchResultItem; + +public interface DashboardSearchService { + public Map> searchResults(String userId, String searchString); + public List getRelatedUsers(String userId); + + public CommonWidgetMeta getWidgetData(String resourceType); + public String saveWidgetDataBulk(CommonWidgetMeta commonWidgetMetaData); + public String saveWidgetData(CommonWidget commonWidgetData); + public String deleteWidgetData(CommonWidget eventWidget); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/DashboardSearchServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/DashboardSearchServiceImpl.java new file mode 100644 index 00000000..412d833d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/DashboardSearchServiceImpl.java @@ -0,0 +1,100 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalapp.portal.transport.CommonWidget; +import org.openecomp.portalapp.portal.transport.CommonWidgetMeta; +import org.openecomp.portalapp.portal.ecomp.model.SearchResultItem; + +@Component +public class DashboardSearchServiceImpl implements DashboardSearchService { + + @Autowired + private DataAccessService dataAccessService; + + public Map> searchResults(String userId, String searchString) { + Map params = new HashMap<>(); + params.put("userId", userId); + params.put("searchQuery", searchString); + // Named query is stored in a *.hbm.xml file, mapped to SearchResultItem + @SuppressWarnings("unchecked") + List list = dataAccessService.executeNamedQuery("searchPortal", params, null); + Map> finalJson = null; + if (list.size() > 0) { + finalJson = new HashMap>(); + for (SearchResultItem thisResult : list) { + List thisList = finalJson.get(thisResult.getCategory().toLowerCase()); + if (thisList == null) + thisList = new ArrayList(); + thisList.add(thisResult); + finalJson.put(thisResult.getCategory().toLowerCase(), thisList); + } + } + return finalJson; + } + + @Override + public List getRelatedUsers(String userId) { + Map params = new HashMap<>(); + params.put("userId", userId); + @SuppressWarnings("unchecked") + List activeUsers = dataAccessService.executeNamedQuery("relatedUsers", params, null); + return activeUsers; + } + + @Override + public CommonWidgetMeta getWidgetData(String resourceType) { + Map params = new HashMap<>(); + params.put("cat", resourceType); + @SuppressWarnings("unchecked") + List widgetItems = (List) dataAccessService.executeNamedQuery("getCommonWidgetItem", params, null); + return new CommonWidgetMeta(resourceType, widgetItems); + } + + @Override + public String saveWidgetDataBulk(CommonWidgetMeta commonMetaWidgetData) { + for (CommonWidget widgetData : commonMetaWidgetData.getItems()) { + widgetData.setCategory(commonMetaWidgetData.getCategory()); + dataAccessService.saveDomainObject(widgetData, null); + } + return "success"; + } + + @Override + public String saveWidgetData(CommonWidget commonWidgetData) { + dataAccessService.saveDomainObject(commonWidgetData, null); + return "success"; + } + + @Override + public String deleteWidgetData(CommonWidget eventWidget) { + dataAccessService.deleteDomainObject(eventWidget, null); + return "success"; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAppCommonServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAppCommonServiceImpl.java new file mode 100644 index 00000000..a5b15e24 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAppCommonServiceImpl.java @@ -0,0 +1,1368 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang.StringUtils; +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.openecomp.portalapp.portal.domain.AdminUserApp; +import org.openecomp.portalapp.portal.domain.AdminUserApplications; +import org.openecomp.portalapp.portal.domain.AppIdAndNameTransportModel; +import org.openecomp.portalapp.portal.domain.AppsResponse; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EPUserAppsManualSortPreference; +import org.openecomp.portalapp.portal.domain.EPUserAppsSortPreference; +import org.openecomp.portalapp.portal.domain.EPWidgetsManualSortPreference; +import org.openecomp.portalapp.portal.domain.EcompApp; +import org.openecomp.portalapp.portal.domain.UserRole; +import org.openecomp.portalapp.portal.domain.UserRoles; +import org.openecomp.portalapp.portal.ecomp.model.AppCatalogItem; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.transport.EPAppsManualPreference; +import org.openecomp.portalapp.portal.transport.EPAppsSortPreference; +import org.openecomp.portalapp.portal.transport.EPDeleteAppsManualSortPref; +import org.openecomp.portalapp.portal.transport.EPWidgetsSortPreference; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.FunctionalMenuItem; +import org.openecomp.portalapp.portal.transport.LocalRole; +import org.openecomp.portalapp.portal.transport.OnboardingApp; +import org.openecomp.portalapp.portal.ueb.EPUebHelper; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.ueb.Helper; +import org.openecomp.portalsdk.core.onboarding.ueb.TopicManager; +import org.openecomp.portalsdk.core.onboarding.util.CipherUtil; +import org.openecomp.portalsdk.core.onboarding.util.PortalApiConstants; +import org.openecomp.portalsdk.core.onboarding.util.PortalApiProperties; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.springframework.beans.factory.annotation.Autowired; + +import com.att.nsa.apiClient.http.HttpException; +import com.att.nsa.cambria.client.CambriaClient.CambriaApiException; +import com.att.nsa.cambria.client.CambriaClientBuilders; +import com.att.nsa.cambria.client.CambriaIdentityManager; +import com.google.common.primitives.Ints; + +public class EPAppCommonServiceImpl implements EPAppService { + + protected String ECOMP_APP_ID = "1"; + protected String SUPER_ADMIN_ROLE_ID = "1"; + protected String ACCOUNT_ADMIN_ROLE_ID = "999"; + protected String RESTRICTED_APP_ROLE_ID = "900"; + + private static final String urlField = "url"; + private static final String nameField = "name"; + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPAppCommonServiceImpl.class); + + @Autowired + AdminRolesService adminRolesService; + @Autowired + private SessionFactory sessionFactory; + @Autowired + private DataAccessService dataAccessService; + @Autowired + EPUebHelper epUebHelper; + + @PostConstruct + private void init() { + SUPER_ADMIN_ROLE_ID = SystemProperties.getProperty(EPCommonSystemProperties.SYS_ADMIN_ROLE_ID); + ACCOUNT_ADMIN_ROLE_ID = SystemProperties.getProperty(EPCommonSystemProperties.ACCOUNT_ADMIN_ROLE_ID); + ECOMP_APP_ID = SystemProperties.getProperty(EPCommonSystemProperties.ECOMP_APP_ID); + RESTRICTED_APP_ROLE_ID = SystemProperties.getProperty(EPCommonSystemProperties.RESTRICTED_APP_ROLE_ID); + } + + @Override + public List getUserAsAdminApps(EPUser user) { + if (adminRolesService.isAccountAdmin(user)) { + String sql = "SELECT * FROM FN_APP join FN_USER_ROLE ON FN_USER_ROLE.APP_ID=FN_APP.APP_ID where " + + "FN_USER_ROLE.USER_ID=" + user.getId() + " AND FN_USER_ROLE.ROLE_ID=" + ACCOUNT_ADMIN_ROLE_ID + + " AND FN_APP.ENABLED = 'Y'"; + logQuery(sql); + try { + @SuppressWarnings("unchecked") + List adminApps = dataAccessService.executeSQLQuery(sql, EPApp.class, null); + return adminApps; + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + return null; + } + } else { + logger.error(EELFLoggerDelegate.errorLogger, + "getUserAsAdminApps: only Account Admin may invoke this function!"); + return new ArrayList(); + } + } + + @Override + public List getUserByOrgUserIdAsAdminApps(String orgUserId) { + String format = "SELECT * FROM FN_APP app INNER JOIN FN_USER_ROLE userrole ON userrole.APP_ID=app.APP_ID " + + "INNER JOIN FN_USER user on user.USER_ID = userrole.USER_ID " + + "WHERE user.org_user_id = '%s' AND userrole.ROLE_ID=" + ACCOUNT_ADMIN_ROLE_ID + + " AND FN_APP.ENABLED = 'Y'"; + + String sql = String.format(format, orgUserId); + logQuery(sql); + + try { + @SuppressWarnings("unchecked") + List adminApps = dataAccessService.executeSQLQuery(sql, EPApp.class, null); + return adminApps; + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + return null; + } + } + + @Override + public List getAppsFullList() { + @SuppressWarnings("unchecked") + List apps = dataAccessService.getList(EPApp.class, null); + return apps; + } + + @Override + public List getEcompAppAppsFullList() { + return transformAppsToEcompApps(getAppsFullList()); + } + + @Override + public List transformAppsToEcompApps(List appsList) { + List ecompAppList = new ArrayList(); + for (EPApp app : appsList) { + EcompApp ecompApp = new EcompApp(); + ecompApp.setId(app.getId()); + ecompApp.setName(app.getName()); + ecompApp.setImageUrl(app.getImageUrl()); + ecompApp.setDescription(app.getDescription()); + ecompApp.setNotes(app.getNotes()); + ecompApp.setUrl(app.getUrl()); + ecompApp.setAlternateUrl(app.getAlternateUrl()); + ecompApp.setUebTopicName(app.getUebTopicName()); + ecompApp.setUebKey(app.getUebKey()); + ecompApp.setUebSecret(app.getUebSecret()); + ecompApp.setEnabled(app.getEnabled()); + ecompApp.setRestrictedApp(app.isRestrictedApp()); + ecompAppList.add(ecompApp); + } + return ecompAppList; + } + + @Override + public EPApp getApp(Long appId) { + try { + @SuppressWarnings("unchecked") + List apps = dataAccessService.getList(EPApp.class, " where id = " + appId, null, null); + return (apps.size() > 0) ? apps.get(0) : null; + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + return null; + } + } + + @SuppressWarnings("unchecked") + @Override + public List getAdminApps(EPUser user) { + if (adminRolesService.isAccountAdmin(user)) { + String format = "SELECT app.APP_ID, app.APP_NAME, app.APP_TYPE FROM FN_APP app inner join FN_USER_ROLE userrole ON userrole.APP_ID=app.APP_ID " + + "where userrole.USER_ID = %d AND userrole.ROLE_ID=" + ACCOUNT_ADMIN_ROLE_ID + + " AND (app.ENABLED = 'Y' OR app.APP_ID=1)"; + String sql = String.format(format, user.getId()); + // sql += " AND app.APP_REST_ENDPOINT IS NOT NULL AND + // app.APP_REST_ENDPOINT <> ''"; + logQuery(sql); + try { + return dataAccessService.executeSQLQuery(sql, AppIdAndNameTransportModel.class, null); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while fetching the adminApps for user " + user.getLoginId(), e); + } + } + return new ArrayList(); + } + + @Override + public EPApp getAppDetail(String appName) { + final Map params = new HashMap(); + try { + params.put("appName", appName); + @SuppressWarnings("unchecked") + List apps = (List) dataAccessService.executeNamedQuery("getAppDetails", params, null); + return (apps.size() > 0) ? apps.get(0) : null; + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + return null; + } + } + + @SuppressWarnings("unchecked") + @Override + public List getAppsForSuperAdminAndAccountAdmin(EPUser user) { + if (adminRolesService.isSuperAdmin(user) || adminRolesService.isAccountAdmin(user)) { + String format = ""; + String sql = ""; + if (adminRolesService.isSuperAdmin(user)) { + format = "SELECT app.APP_ID, app.APP_NAME, app.APP_TYPE FROM FN_APP app " + + "where app.ENABLED = 'Y' AND app.app_type = 1"; + } else { + format = "SELECT app.APP_ID, app.APP_NAME, APP_TYPE FROM FN_APP app inner join FN_USER_ROLE userrole ON userrole.APP_ID=app.APP_ID " + + "where userrole.USER_ID = %d AND userrole.ROLE_ID=" + ACCOUNT_ADMIN_ROLE_ID + + " AND app.ENABLED = 'Y' AND app.app_type = 1"; + } + sql = String.format(format, user.getId()); + // sql += " AND app.APP_REST_ENDPOINT IS NOT NULL AND + // app.APP_REST_ENDPOINT <> ''"; + logQuery(sql); + try { + return dataAccessService.executeSQLQuery(sql, AppIdAndNameTransportModel.class, null); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while fetching the adminApps for user " + user.getLoginId(), e); + } + } + return new ArrayList(); + } + + protected void logQuery(String sql) { + logger.debug(EELFLoggerDelegate.debugLogger, "logQuery: " + sql); + } + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + + public void setDataAccessService(DataAccessService dataAccessService) { + this.dataAccessService = dataAccessService; + } + + @Override + public List getAppsAdmins() { + String sql = "SELECT apps.APP_NAME, apps.APP_ID, user.USER_ID, user.FIRST_NAME, user.LAST_NAME, user.org_user_id FROM fn_user_role userrole " + + "INNER JOIN fn_user user ON user.USER_ID = userrole.USER_ID " + + "INNER JOIN fn_app apps ON apps.APP_ID = userrole.APP_ID " + "WHERE userrole.ROLE_ID = " + + ACCOUNT_ADMIN_ROLE_ID + " AND (apps.ENABLED = 'Y' OR apps.APP_ID=1)"; + logQuery(sql); + try { + @SuppressWarnings("unchecked") + List adminApps = dataAccessService.executeSQLQuery(sql, AdminUserApp.class, null); + // DataAccessService does not use generic types. + return aggregateRowsResultsByUserId(adminApps); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + return null; + } + } + + private List aggregateRowsResultsByUserId(List adminApps) { + HashMap adminUserApplications = new HashMap(); + for (AdminUserApp app : adminApps) { + Long userId = app.getUser_Id(); + if (adminUserApplications.get(userId) == null) + adminUserApplications.put(userId, new AdminUserApplications(app)); + else + adminUserApplications.get(userId).addApp(app.getAppId(), app.getAppName()); + } + return new ArrayList(adminUserApplications.values()); + } + + @Override + public List getAllApps(Boolean all) { + // If all is true, return both active and inactive apps. Otherwise, just + // active apps. + @SuppressWarnings("unchecked") + // Sort the list by application name so the drop-down looks pretty. + List apps = all + ? (List) dataAccessService.getList(EPApp.class, " where id != " + ECOMP_APP_ID, "name", null) + : (List) dataAccessService.getList(EPApp.class, + " where ( enabled = 'Y' or id = " + ECOMP_APP_ID + ")", "name", null); + + List appsModified = new ArrayList(); + for (EPApp app : apps) { + appsModified.add(new AppsResponse(app.getId(), app.getName(), app.isRestrictedApp(), app.getEnabled())); + } + return appsModified; + } + + @Override + public UserRoles getUserProfile(String loginId) { + String format = "SELECT DISTINCT user.USER_ID, role.ROLE_ID, user.org_user_id, user.FIRST_NAME, user.LAST_NAME, role.ROLE_NAME FROM fn_user_role userrole " + + "INNER JOIN fn_user user ON user.USER_ID = userrole.USER_ID " + + "INNER JOIN fn_role role ON role.ROLE_ID = userrole.ROLE_ID " + + "WHERE user.org_user_id = \"%s\" and (userrole.app_id = 1 or role.role_id = " + ACCOUNT_ADMIN_ROLE_ID + + ") "; + String sql = String.format(format, loginId); + logQuery(sql); + @SuppressWarnings("unchecked") + List userRoleList = dataAccessService.executeSQLQuery(sql, UserRole.class, null); + ArrayList usersRolesList = aggregateUserProfileRowsResultsByRole(userRoleList); + if (usersRolesList == null || usersRolesList.size() < 1) + return null; + + return usersRolesList.get(0); + } + + @Override + public UserRoles getUserProfileNormalized(EPUser user) { + // Check database. + UserRoles userAndRoles = getUserProfile(user.getLoginId()); + // If no roles are defined, treat this user as a guest. + if (user.isGuest() || userAndRoles == null) { + logger.debug(EELFLoggerDelegate.debugLogger, "getUserProfile: treating user {} as guest", + user.getLoginId()); + UserRole userRole = new UserRole(); + userRole.setUser_Id(user.getId()); + userRole.setOrgUserId(user.getLoginId()); + userRole.setFirstName(user.getFirstName()); + userRole.setLastName(user.getLastName()); + userRole.setRoleId(-1L); + userRole.setRoleName("Guest"); + userRole.setUser_Id(-1L); + userAndRoles = new UserRoles(userRole); + } + + return userAndRoles; + } + + protected ArrayList aggregateUserProfileRowsResultsByRole(List userRoleList) { + HashMap userRoles = new HashMap(); + for (UserRole user : userRoleList) { + String orgUserId = user.getOrgUserId(); + if (userRoles.get(orgUserId) == null) + userRoles.put(orgUserId, new UserRoles(user)); + else + userRoles.get(orgUserId).addRole(user.getRoleName()); + } + return new ArrayList(userRoles.values()); + } + + private boolean isRestrictedApp(Long appId) { + EPApp app = getApp(appId); + return app.isRestrictedApp(); + } + + // For the functional menu edit + @Override + public List getAppRoles(Long appId) { + String sql = ""; + if (isRestrictedApp(appId)) { + sql = "SELECT ROLE_ID, ROLE_NAME from FN_ROLE where ROLE_ID = '" + RESTRICTED_APP_ROLE_ID + "'"; + } else { + sql = "SELECT ROLE_ID, ROLE_NAME from FN_ROLE where APP_ID = '" + appId + "'"; + } + logQuery(sql); + @SuppressWarnings("unchecked") + List appRoles = dataAccessService.executeSQLQuery(sql, LocalRole.class, null); + return appRoles; + } + + protected String userAppsQuery(EPUser user) { + StringBuilder query = new StringBuilder(); + if (adminRolesService.isSuperAdmin(user)) { + query.append("SELECT * FROM FN_APP where FN_APP.ENABLED = 'Y' ORDER BY APP_NAME"); + } else { + query.append("SELECT * FROM FN_APP join FN_USER_ROLE ON FN_USER_ROLE.APP_ID = FN_APP.APP_ID where "); + query.append( + "FN_USER_ROLE.USER_ID = " + user.getId() + " AND FN_USER_ROLE.ROLE_ID != " + SUPER_ADMIN_ROLE_ID); + query.append(" AND FN_APP.ENABLED = 'Y'"); + } + return query.toString(); + } + + protected FieldsValidator onboardingAppFieldsChecker(OnboardingApp onboardingApp) { + FieldsValidator fieldsValidator = new FieldsValidator(); + if (onboardingApp.name == null || onboardingApp.name.length() == 0 || onboardingApp.url == null + || onboardingApp.url.length() == 0 || onboardingApp.restrictedApp == null + || onboardingApp.isOpen == null || onboardingApp.isEnabled == null + || (onboardingApp.id != null && onboardingApp.id.equals(ECOMP_APP_ID)) + // For a normal app (appType==1), these fields must be filled + // in. + // For a restricted app (appType==2), they will be empty. + || ((!onboardingApp.restrictedApp) + && (onboardingApp.username == null || onboardingApp.username.length() == 0 + || onboardingApp.appPassword == null || onboardingApp.appPassword.length() == 0))) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_BAD_REQUEST); + } + return fieldsValidator; + } + + @Override + public List getUserApps(EPUser user) { + List openApps = getOpenApps(); + + if (user.isGuest()) { + return openApps; + } else { + String sql = userAppsQuery(user); + logQuery(sql); + + // TreeSet distinctApps = new TreeSet(); + List appsList = new ArrayList<>(); + @SuppressWarnings("unchecked") + List adminApps = dataAccessService.executeSQLQuery(sql, EPApp.class, null); + HashSet appSet = new HashSet<>(); + for (EPApp app : adminApps) { + appSet.add(app); + appsList.add(app); + } + + for (EPApp app : openApps) { + if (!appSet.contains(app)) + appsList.add(app); + } + + return appsList; + } + } + + @Override + public List getPersAdminApps(EPUser user) { + final Map params = new HashMap<>(); + params.put("userId", user.getId()); + // Named query is stored in EP.hbm.xml, mapped to EPApp + @SuppressWarnings("unchecked") + List list = dataAccessService.executeNamedQuery("getPersAdminApps", params, null); + return list; + } + + @Override + public List getPersUserApps(EPUser user) { + final Map params = new HashMap<>(); + params.put("userId", user.getId()); + // Named query is stored in EP.hbm.xml, mapped to EPApp + @SuppressWarnings("unchecked") + List list = dataAccessService.executeNamedQuery("getPersUserApps", params, null); + return list; + } + + /* + * (non-Javadoc) + * + * @see + * org.openecomp.portalapp.portal.service.EPAppService#getAppCatalog(com.att + * .fusionapp.ecomp.portal.domain.EPUser) + */ + @Override + public List getUserAppCatalog(EPUser user) { + final Map params = new HashMap<>(); + params.put("userId", user.getId()); + // Named query is stored in EP.hbm.xml, mapped to AppCatalogItem + @SuppressWarnings("unchecked") + List list = dataAccessService.executeNamedQuery("getUserAppCatalog", params, null); + return list; + } + + /* + * (non-Javadoc) + * + * @see + * org.openecomp.portalapp.portal.service.EPAppService#getAdminAppCatalog( + * org.openecomp.portalapp.portal.domain.EPUser) + */ + @Override + public List getAdminAppCatalog(EPUser user) { + final Map params = new HashMap<>(); + params.put("userId", user.getId()); + // Named query is stored in EP.hbm.xml, mapped to AppCatalogItem + @SuppressWarnings("unchecked") + List list = dataAccessService.executeNamedQuery("getAdminAppCatalog", params, null); + return list; + } + + private List getOpenApps() { + @SuppressWarnings("unchecked") + List openApps = dataAccessService.getList(EPApp.class, " where open='Y' and enabled='Y'", null, null); + return openApps; + } + + @SuppressWarnings("unchecked") + @Override + public List getAppsOrderByName(EPUser user) { + final Map params = new HashMap<>(); + List sortedAppsByName = null; + try { + if (adminRolesService.isSuperAdmin(user)) { + params.put("userId", user.getId()); + sortedAppsByName = dataAccessService.executeNamedQuery("getPersAdminAppsOrderByName", params, null); + } else { + params.put("userId", user.getId()); + sortedAppsByName = dataAccessService.executeNamedQuery("getPersUserAppsOrderByName", params, null); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAppsOrderByName failed", e); + } + return sortedAppsByName; + } + + @SuppressWarnings("unchecked") + @Override + public List getAppsOrderByLastUsed(EPUser user) { + + final Map params = new HashMap<>(); + List sortedAppsByLastUsed = new ArrayList(); + List finalsortedAppsByLastUsed = new ArrayList(); + try { + if (adminRolesService.isSuperAdmin(user)) { + params.put("userId", user.getId()); + sortedAppsByLastUsed = dataAccessService.executeNamedQuery("getAdminAppsOrderByLastUsed", params, null); + } else { + params.put("userId", user.getId()); + sortedAppsByLastUsed = dataAccessService.executeNamedQuery("getUserAppsOrderByLastUsed", params, null); + } + Set epAppSet = new HashSet(); + for (EPApp eapp : sortedAppsByLastUsed) + if (!epAppSet.contains(eapp.getName())) { + finalsortedAppsByLastUsed.add(eapp); + epAppSet.add(eapp.getName()); + } + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAppsOrderByLastUsed failed", e); + } + return finalsortedAppsByLastUsed; + } + + @SuppressWarnings("unchecked") + @Override + public List getAppsOrderByMostUsed(EPUser user) { + final Map params = new HashMap<>(); + List sortedAppsByMostUsed = new ArrayList(); + List finalsortedAppsByMostUsed = new ArrayList(); + try { + if (adminRolesService.isSuperAdmin(user)) { + params.put("userId", user.getId()); + sortedAppsByMostUsed = dataAccessService.executeNamedQuery("getAdminAppsOrderByMostUsed", params, null); + } else { + params.put("userId", user.getId()); + sortedAppsByMostUsed = dataAccessService.executeNamedQuery("getUserAppsOrderByMostUsed", params, null); + } + Set epAppSet = new HashSet(); + + for (EPApp eapp : sortedAppsByMostUsed) { + if (!epAppSet.contains(eapp.getName())) { + finalsortedAppsByMostUsed.add(eapp); + epAppSet.add(eapp.getName()); + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAppsOrderByMostUsed failed", e); + } + + return finalsortedAppsByMostUsed; + } + + /* + * This Method retrieves the User Apps by Sort Manual Preference + * + * @param: user--contains LoggedIn User Data + */ + @SuppressWarnings("unchecked") + @Override + public List getAppsOrderByManual(EPUser user) { + final Map params = new HashMap<>(); + List sortedAppsByManual = new ArrayList(); + List finalsortedAppsByManual = new ArrayList(); + try { + if (adminRolesService.isSuperAdmin(user)) { + params.put("userId", user.getId()); + sortedAppsByManual = dataAccessService.executeNamedQuery("getAdminAppsOrderByManual", params, null); + } else { + params.put("userId", user.getId()); + sortedAppsByManual = dataAccessService.executeNamedQuery("getUserAppsOrderByManual", params, null); + } + Set epAppSet = new HashSet(); + + for (EPApp eapp : sortedAppsByManual) { + if (!epAppSet.contains(eapp.getName())) { + finalsortedAppsByManual.add(eapp); + epAppSet.add(eapp.getName()); + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getAppsOrderByManual failed", e); + } + return finalsortedAppsByManual; + } + + @Override + public List getOnboardingApps() { + @SuppressWarnings("unchecked") + List apps = dataAccessService.getList(EPApp.class, " where id!=" + ECOMP_APP_ID, null, null); + List onboardingAppsList = new ArrayList(); + for (EPApp app : apps) { + OnboardingApp onboardingApp = new OnboardingApp(); + createOnboardingFromApp(app, onboardingApp); + onboardingAppsList.add(onboardingApp); + } + return onboardingAppsList; + } + + @Override + public List getEnabledNonOpenOnboardingApps() { + @SuppressWarnings("unchecked") + List apps = dataAccessService.getList(EPApp.class, + " where enabled = true and open = false and id!=" + ECOMP_APP_ID, null, null); + List onboardingAppsList = new ArrayList(); + for (EPApp app : apps) { + OnboardingApp onboardingApp = new OnboardingApp(); + createOnboardingFromApp(app, onboardingApp); + onboardingAppsList.add(onboardingApp); + } + return onboardingAppsList; + } + + @SuppressWarnings("unchecked") + private void validateOnboardingApp(OnboardingApp onboardingApp, FieldsValidator fieldsValidator) { + boolean duplicatedUrl = false; + boolean duplicatedName = false; + List apps; + if (onboardingApp.id == null) { + apps = dataAccessService.getList(EPApp.class, + " where url = '" + onboardingApp.url + "' or name = '" + onboardingApp.name + "'", null, null); + } else { + apps = dataAccessService.getList(EPApp.class, " where id = " + onboardingApp.id + " or url = '" + + onboardingApp.url + "' or name = '" + onboardingApp.name + "'", null, null); + } + for (EPApp app : apps) { + if (onboardingApp.id != null && onboardingApp.id.equals(app.getId())) { + continue; + } + if (!duplicatedUrl && app.getUrl().equalsIgnoreCase(onboardingApp.url)) { + duplicatedUrl = true; + if (duplicatedName) { + break; + } + } + if (!duplicatedName && app.getName().equalsIgnoreCase(onboardingApp.name)) { + duplicatedName = true; + if (duplicatedUrl) { + break; + } + } + } + if (duplicatedUrl || duplicatedName) { + if (duplicatedUrl) { + fieldsValidator.addProblematicFieldName(urlField); + } + if (duplicatedName) { + fieldsValidator.addProblematicFieldName(nameField); + } + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_CONFLICT); + fieldsValidator.errorCode = new Long(EPCommonSystemProperties.DUBLICATED_FIELD_VALUE_ECOMP_ERROR); + } + } + + @Override + public FieldsValidator modifyOnboardingApp(OnboardingApp modifiedOnboardingApp, EPUser user) { + logger.debug(EELFLoggerDelegate.debugLogger, "LR: entering modifyOnboardingApp"); + FieldsValidator fieldsValidator = onboardingAppFieldsChecker(modifiedOnboardingApp); + if (fieldsValidator.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + validateOnboardingApp(modifiedOnboardingApp, fieldsValidator); + } + if (fieldsValidator.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + if (modifiedOnboardingApp.id != null) { + updateApp(modifiedOnboardingApp.id, modifiedOnboardingApp, fieldsValidator, user); + } else { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_BAD_REQUEST); + } + } + return fieldsValidator; + } + + @Override + public FieldsValidator addOnboardingApp(OnboardingApp newOnboardingApp, EPUser user) { + FieldsValidator fieldsValidator = onboardingAppFieldsChecker(newOnboardingApp); + if (fieldsValidator.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + validateOnboardingApp(newOnboardingApp, fieldsValidator); + } + if (fieldsValidator.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + if (newOnboardingApp.id == null) { + updateApp(null, newOnboardingApp, fieldsValidator, user); + } else { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_BAD_REQUEST); + } + } + return fieldsValidator; + } + + @Override + public FieldsValidator deleteOnboardingApp(EPUser user, Long appid) { + FieldsValidator fieldsValidator = new FieldsValidator(); + if (!adminRolesService.isSuperAdmin(user)) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_FORBIDDEN); + return fieldsValidator; + } + Boolean result = false; + Session localSession = null; + Transaction transaction = null; + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + + // 1) Remove the URL for any functional menu item associated with + // this app + String sql = "UPDATE fn_menu_functional m, fn_menu_functional_roles mr SET m.url='' " + + " WHERE m.menu_id=mr.menu_id " + " AND mr.app_id='" + appid + "'"; + logQuery(sql); + Query query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove any favorites associated with a menu item that is + // associated with this app + sql = "Delete from fn_menu_favorites " + " using fn_menu_favorites inner join fn_menu_functional_roles " + + " where fn_menu_functional_roles.app_id='" + appid + "' " + + " AND fn_menu_functional_roles.menu_id=fn_menu_favorites.menu_id"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove all role, appid records from fn_menu_functional_role + // that are associated with this app + sql = "delete from fn_menu_functional_roles where app_id='" + appid + "'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove all records from fn_user_role associated with this app + sql = "delete from fn_user_role where app_id='" + appid + "'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove any widgets associated with this app + sql = "delete from ep_widget_catalog_role where app_id='" + appid + "'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove any roles associated with this app + sql = "delete from ep_role_notification " + " using ep_role_notification inner join fn_role " + + " where fn_role.app_id='" + appid + "' " + " and ep_role_notification.role_id= fn_role.role_id"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove all records from fn_role associated with this app + sql = "delete from fn_role where app_id='" + appid + "'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove app contact us entries + sql = "delete from fn_app_contact_us where app_id='" + appid + "'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove any widgets associated with this app + sql = "delete from fn_widget where app_id='" + appid + "'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove rows in the app personalization selection table + sql = "delete from fn_pers_user_app_sel where app_id='" + appid + "'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Remove rows in the app personalization sort table + sql = "delete from ep_pers_user_app_man_sort where app_id='" + appid + "'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + // Delete the app + sql = "delete from fn_app where app_id='" + appid + "'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + + transaction.commit(); + result = true; + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "deleteOnboardingApp failed", e); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError); + EcompPortalUtils.rollbackTransaction(transaction, "deleteOnboardingApp rollback, exception = " + e); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "deleteOnboardingApp"); + } + if (!result) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + return fieldsValidator; + } + + private static Object syncRests = new Object(); + + // An app has been enabled/disabled. Must enable/disable all associated + // functional menu items. + private void setFunctionalMenuItemsEnabled(Session localSession, Boolean enabled, Long appId) { + String active_yn = enabled ? "Y" : "N"; + String sql = "SELECT m.menu_id, m.column_num, m.text, m.parent_menu_id, m.url, m.active_yn " + + "FROM fn_menu_functional m, fn_menu_functional_roles r " + "WHERE m.menu_id = r.menu_id " + + " AND r.app_id = '" + appId + "' "; + logQuery(sql); + @SuppressWarnings("unchecked") + List menuItems = dataAccessService.executeSQLQuery(sql, FunctionalMenuItem.class, null); + for (FunctionalMenuItem menuItem : menuItems) { + FunctionalMenuItem myMenuItem = (FunctionalMenuItem) localSession.get(FunctionalMenuItem.class, + menuItem.menuId); + myMenuItem.active_yn = active_yn; + localSession.save(myMenuItem); + } + } + + // Attention! If (appId == null) we use this function to create application + // otherwise we use it to modify existing application + private void updateApp(Long appId, OnboardingApp onboardingApp, FieldsValidator fieldsValidator, EPUser user) { + logger.debug(EELFLoggerDelegate.debugLogger, "LR: entering updateApp"); + // Separate out the code for a restricted app, since it doesn't need any + // of the UEB code. + if (onboardingApp.restrictedApp) { + boolean result = false; + Session localSession = null; + Transaction transaction = null; + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + EPApp app; + if (appId == null) { + app = new EPApp(); + } else { + app = (EPApp) localSession.get(EPApp.class, appId); + if (app == null || app.getId() == null) { // App is already + // deleted! + transaction.commit(); + localSession.close(); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_NOT_FOUND); + return; + } + } + createAppFromOnboarding(app, onboardingApp, localSession); + localSession.saveOrUpdate(app); + // Enable or disable all menu items associated with this app + setFunctionalMenuItemsEnabled(localSession, onboardingApp.isEnabled, appId); + transaction.commit(); + result = true; + } catch (Exception e) { + EcompPortalUtils.rollbackTransaction(transaction, + "updateApp rollback, exception = " + EcompPortalUtils.getStackTrace(e)); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "updateApp"); + } + if (!result) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + + } else { + synchronized (syncRests) { + boolean result = false; + Session localSession = null; + Transaction transaction = null; + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + EPApp app; + if (appId == null) { + app = new EPApp(); + // ------------------------------------------------------------------------------------------- + // Register this App with the UEB communication server. + // Save + // the App's unique mailbox/topic + // name and keys to the FN_APP table. The App's mailbox + // and + // keys will be visible to the + // admin on the ECOMP portal. + // ------------------------------------------------------------------------------------------- + TopicManager topicManager = new TopicManager(); + final CambriaIdentityManager im = new CambriaClientBuilders.IdentityManagerBuilder() + .usingHosts(Helper.uebUrlList()).build(); + com.att.nsa.apiClient.credentials.ApiCredential credential = im.createApiKey(user.getEmail(), + "ECOMP Portal Owner"); + String appKey = credential.getApiKey(); + String appSecret = credential.getApiSecret(); + String appMailboxName = null; + + int maxNumAttemptsToCreateATopic = 3; + boolean successfullyCreatedMailbox = false; + for (int i = 0; i < maxNumAttemptsToCreateATopic; i++) { + appMailboxName = "ECOMP-PORTAL-OUTBOX-" + (int) (Math.random() * 100000.0); + + try { + topicManager.createTopic( + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_KEY), + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_SECRET), + appMailboxName, "ECOMP outbox for app" + onboardingApp.name); + successfullyCreatedMailbox = true; + logger.debug(EELFLoggerDelegate.debugLogger, + "Successfully created " + appMailboxName + " for App " + onboardingApp.name); + logger.debug(EELFLoggerDelegate.debugLogger, " Key = " + appKey + " Secret = " + + appSecret + " generated using = " + user.getEmail()); + break; + } catch (HttpException e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUebConnectionError, e); + if (e.getStatusCode() == 409) { + logger.error(EELFLoggerDelegate.errorLogger, "Topic/mailbox " + appMailboxName + + " already exists. Will try using a different name", e); + } else { + logger.error(EELFLoggerDelegate.errorLogger, "HttpException when onboarding App: ", + e); + } + } + } + + if (successfullyCreatedMailbox) { + onboardingApp.setUebTopicName(appMailboxName); + onboardingApp.setUebKey(appKey); + onboardingApp.setUebSecret(appSecret); + + try { + /* + * EP is a publisher to this App's new mailbox + */ + topicManager.addPublisher( + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_KEY), + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_SECRET), + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_KEY), + appMailboxName); + + /* + * This App is a subscriber of its own mailbox + */ + topicManager.addSubscriber( + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_KEY), + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_SECRET), appKey, + appMailboxName); + + /* + * This App is a publisher to EP + */ + topicManager.addPublisher( + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_KEY), + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_SECRET), appKey, + PortalApiProperties.getProperty(PortalApiConstants.ECOMP_PORTAL_INBOX_NAME)); + } catch (HttpException | CambriaApiException | IOException e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUebRegisterOnboardingAppError, e); + logger.error(EELFLoggerDelegate.errorLogger, + "Error when configuring Publisher/Subscriber for App's new mailbox", e); + transaction.commit(); + localSession.close(); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_CONFLICT); + return; + } + } else { + transaction.commit(); + localSession.close(); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_CONFLICT); + return; + } + } else { + app = (EPApp) localSession.get(EPApp.class, appId); + if (app == null || app.getId() == null) { + // App is already deleted! + transaction.commit(); + localSession.close(); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_NOT_FOUND); + return; + } + } + logger.debug(EELFLoggerDelegate.debugLogger, "LR: about to call createAppFromOnboarding"); + createAppFromOnboarding(app, onboardingApp, localSession); + logger.debug(EELFLoggerDelegate.debugLogger, + "LR: updateApp: finished calling createAppFromOnboarding"); + localSession.saveOrUpdate(app); + logger.debug(EELFLoggerDelegate.debugLogger, + "LR: updateApp: finished calling localSession.saveOrUpdate"); + // Enable or disable all menu items associated with this app + setFunctionalMenuItemsEnabled(localSession, onboardingApp.isEnabled, appId); + logger.debug(EELFLoggerDelegate.debugLogger, + "LR: updateApp: finished calling setFunctionalMenuItemsEnabled"); + transaction.commit(); + logger.debug(EELFLoggerDelegate.debugLogger, "LR: updateApp: finished calling transaction.commit"); + epUebHelper.addPublisher(app); + logger.debug(EELFLoggerDelegate.debugLogger, + "LR: updateApp: finished calling epUebHelper.addPublisher"); + result = true; + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "updateApp failed", e); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUebRegisterOnboardingAppError, e); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + EcompPortalUtils.rollbackTransaction(transaction, + "updateApp rollback, exception = " + EcompPortalUtils.getStackTrace(e)); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "updateApp"); + } + if (!result) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + } + } + + /** + * Populates a transport model of the application from a database row model. + * Leaves out the thumbnail because the FE fetches images via a different + * API. + * + * @param app + * Model of database row + * @param onboardingApp + * Model for transport as JSON + */ + @Override + public void createOnboardingFromApp(EPApp app, OnboardingApp onboardingApp) { + onboardingApp.id = app.getId(); + onboardingApp.name = app.getName(); + onboardingApp.imageUrl = app.getImageUrl(); + onboardingApp.description = app.getDescription(); + onboardingApp.notes = app.getNotes(); + onboardingApp.url = app.getUrl(); + onboardingApp.alternateUrl = app.getAlternateUrl(); + onboardingApp.restUrl = app.getAppRestEndpoint(); + onboardingApp.isOpen = app.getOpen(); + onboardingApp.isEnabled = app.getEnabled(); + onboardingApp.username = app.getUsername(); + onboardingApp.appPassword = decryptedPassword(app.getAppPassword(), app); + onboardingApp.uebTopicName = app.getUebTopicName(); + onboardingApp.uebKey = app.getUebKey(); + onboardingApp.uebSecret = app.getUebSecret(); + onboardingApp.setRestrictedApp(app.isRestrictedApp()); + // if (app.getThumbnail() != null) + // onboardingApp.thumbnail = new + // String(Base64.getEncoder().encode(app.getThumbnail())); + } + + /** + * Creates a database object for an application from an uploaded transport + * model. Must decode the thumbnail, if any. + * + * @param app + * @param onboardingApp + * @param localSession + * @return The first argument. + */ + protected EPApp createAppFromOnboarding(EPApp app, OnboardingApp onboardingApp, Session localSession) { + app.setName(onboardingApp.name); + app.setDescription(onboardingApp.description); + app.setNotes(onboardingApp.notes); + app.setUrl(onboardingApp.url); + app.setAlternateUrl(onboardingApp.alternateUrl); + app.setAppRestEndpoint(onboardingApp.restUrl); + app.setOpen(onboardingApp.isOpen); + app.setEnabled(onboardingApp.isEnabled); + app.setUsername(onboardingApp.username); + app.setAppPassword(this.encryptedPassword(onboardingApp.appPassword, app)); + app.setUebTopicName(onboardingApp.uebTopicName); + app.setUebKey(onboardingApp.uebKey); + app.setUebSecret(onboardingApp.uebSecret); + app.setRestrictedApp(onboardingApp.restrictedApp); + if (!StringUtils.isEmpty(onboardingApp.thumbnail)) { + logger.debug(EELFLoggerDelegate.debugLogger, "createAppFromOnboarding: onboarding thumbnail is NOT empty"); + String[] splitBase64Thumbnail = onboardingApp.thumbnail.split("base64,"); + logger.debug(EELFLoggerDelegate.debugLogger, + "createAppFromOnboarding: length of splitBase64Thumbnail: " + splitBase64Thumbnail.length); + if (splitBase64Thumbnail.length > 1) { + // This occurs when we have a new image, not an existing image + byte[] decodedImage = Base64.getDecoder().decode(splitBase64Thumbnail[1].getBytes()); + logger.debug(EELFLoggerDelegate.debugLogger, "createAppFromOnboarding: finished calling decode"); + // This is basically a boolean indicator that an image is + // present. + app.setImageUrl(constructImageName(onboardingApp)); + app.setThumbnail(decodedImage); + } + } else if (app.getThumbnail() != null) { + // The thumbnail that came in from the json is empty; the previous + // thumbnail is NOT empty. Must delete it. + logger.debug(EELFLoggerDelegate.debugLogger, + "createAppFromOnboarding: onboarding thumbnail is empty; db thumbnail is NOT null"); + app.setImageUrl(null); + app.setThumbnail(null); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, + "createAppFromOnboarding: onboarding thumbnail is empty; db thumbnail is null"); + } + return app; + } + + protected String constructImageName(OnboardingApp onboardingApp) { + return "portal_" + String.valueOf(onboardingApp.url.hashCode() + "_" + (int) (Math.random() * 100000.0)) + + ".png"; + } + + // Don't encrypt or decrypt the password if it is null or the empty string + private String decryptedPassword(String encryptedAppPwd, EPApp app) { + String result = ""; + if (encryptedAppPwd != null & encryptedAppPwd.length() > 0) { + try { + result = CipherUtil.decrypt(encryptedAppPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "decryptedPassword failed for app " + app.getName(), e); + } + } + return result; + } + + protected String encryptedPassword(String decryptedAppPwd, EPApp app) { + String result = ""; + if (decryptedAppPwd != null & decryptedAppPwd.length() > 0) { + try { + result = CipherUtil.encrypt(decryptedAppPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "encryptedPassword failed for app " + app.getName(), e); + } + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + public FieldsValidator saveWidgetsSortManual(List widgetsSortManual, EPUser user) { + FieldsValidator fieldsValidator = new FieldsValidator(); + final Map params = new HashMap<>(); + List epManualWidgets = new ArrayList(); + + try { + params.put("userId", user.getId()); + epManualWidgets = dataAccessService.executeNamedQuery("userWidgetManualSortPrfQuery", params, null); + Map existingWidgetsIds = new HashMap(); + for (EPWidgetsManualSortPreference userWidgetManualPref : epManualWidgets) { + existingWidgetsIds.put(userWidgetManualPref.getWidgetId(), userWidgetManualPref); + } + for (EPWidgetsSortPreference epWidgetsManPref : widgetsSortManual) { + if (epWidgetsManPref.getWidgetid() != null) { + Long widgetid = epWidgetsManPref.getWidgetid(); + if (existingWidgetsIds.containsKey(widgetid)) { + EPWidgetsManualSortPreference epWidgetsManualSort = existingWidgetsIds.get(widgetid); + epWidgetsManualSort.setWidgetRow(epWidgetsManPref.getRow()); + epWidgetsManualSort.setWidgetCol(epWidgetsManPref.getCol()); + epWidgetsManualSort.setWidgetWidth(epWidgetsManPref.getSizeX()); + epWidgetsManualSort.setWidgetHeight(epWidgetsManPref.getSizeY()); + HashMap additionalUpdateParam = new HashMap(); + additionalUpdateParam.put("userId", epWidgetsManualSort.getUserId()); + dataAccessService.saveDomainObject(epWidgetsManualSort, additionalUpdateParam); + } else { + EPWidgetsManualSortPreference epWidgetsManualSort = new EPWidgetsManualSortPreference(); + epWidgetsManualSort.setWidgetId(epWidgetsManPref.getWidgetid()); + epWidgetsManualSort.setWidgetRow(epWidgetsManPref.getRow()); + epWidgetsManualSort.setWidgetCol(epWidgetsManPref.getCol()); + epWidgetsManualSort.setWidgetWidth(epWidgetsManPref.getSizeX()); + epWidgetsManualSort.setWidgetHeight(epWidgetsManPref.getSizeY()); + epWidgetsManualSort.setUserId(Ints.checkedCast(user.getId())); + dataAccessService.saveDomainObject(epWidgetsManualSort, null); + } + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_OK); + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "saveWidgetsSortManual failed", e); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + return fieldsValidator; + } + + @SuppressWarnings("unchecked") + @Override + public FieldsValidator deleteUserWidgetSortPref(List delWidgetSortPref, EPUser user) { + FieldsValidator fieldsValidator = new FieldsValidator(); + final Map params = new HashMap<>(); + List epWidgets = new ArrayList(); + try { + params.put("userId", user.getId()); + epWidgets = dataAccessService.executeNamedQuery("userWidgetManualSortPrfQuery", params, null); + Map existingWidgetIds = new HashMap(); + for (EPWidgetsManualSortPreference userWidgetSortPref : epWidgets) { + existingWidgetIds.put(userWidgetSortPref.getWidgetId(), userWidgetSortPref); + } + for (EPWidgetsSortPreference delEpWidgetsManPref : delWidgetSortPref) { + if (delEpWidgetsManPref.getWidgetid() != null) { + Long widgetId = delEpWidgetsManPref.getWidgetid(); + if (existingWidgetIds.containsKey(widgetId)) { + dataAccessService.deleteDomainObjects(EPWidgetsManualSortPreference.class, + "widget_id=" + widgetId + " AND user_id=" + user.getId(), null); + } + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_OK); + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "deleteUserWidgetSortPref failed", e); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + return fieldsValidator; + } + + /* + * This Method Stores the Sort Order of User Apps by Sort Manual Preference + * + * @param: appsSortManual--contains User Apps Data + * + * @param: user--contains LoggedIn User Data + */ + @SuppressWarnings("unchecked") + @Override + public FieldsValidator saveAppsSortManual(List appsSortManual, EPUser user) { + FieldsValidator fieldsValidator = new FieldsValidator(); + final Map params = new HashMap<>(); + List epManualApps = new ArrayList(); + + try { + params.put("userId", user.getId()); + epManualApps = dataAccessService.executeNamedQuery("userAppsManualSortPrfQuery", params, null); + Map existingAppIds = new HashMap(); + for (EPUserAppsManualSortPreference userAppManualPref : epManualApps) { + existingAppIds.put(userAppManualPref.getAppId(), userAppManualPref); + } + for (EPAppsManualPreference epAppsManPref : appsSortManual) { + if (epAppsManPref.getAppid() != null) { + Long appid = epAppsManPref.getAppid(); + if (existingAppIds.containsKey(appid)) { + EPUserAppsManualSortPreference epAppsManualSort = existingAppIds.get(appid); + epAppsManualSort + .setAppManualSortOrder((epAppsManPref.getCol() + (6 * epAppsManPref.getRow())) + 1); + HashMap additionalUpdateParam = new HashMap(); + additionalUpdateParam.put("userId", epAppsManualSort.getUserId()); + dataAccessService.saveDomainObject(epAppsManualSort, additionalUpdateParam); + } else { + EPUserAppsManualSortPreference epAppsManualSort = new EPUserAppsManualSortPreference(); + epAppsManualSort.setAppId(epAppsManPref.getAppid()); + epAppsManualSort + .setAppManualSortOrder((epAppsManPref.getCol() + (6 * epAppsManPref.getRow())) + 1); + epAppsManualSort.setUserId(Ints.checkedCast(user.getId())); + dataAccessService.saveDomainObject(epAppsManualSort, null); + } + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_OK); + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "saveAppsSortManual failed", e); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + return fieldsValidator; + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalapp.portal.service.EPAppService# + * deleteUserAppSortManual(java.lang.String, + * org.openecomp.portalapp.portal.domain.EPUser) + */ + @SuppressWarnings("unchecked") + @Override + public FieldsValidator deleteUserAppSortManual(EPDeleteAppsManualSortPref delAppSortManual, EPUser user) { + FieldsValidator fieldsValidator = new FieldsValidator(); + final Map params = new HashMap<>(); + List epManualApps = new ArrayList(); + try { + params.put("userId", user.getId()); + epManualApps = dataAccessService.executeNamedQuery("userAppsManualSortPrfQuery", params, null); + Map existingAppIds = new HashMap(); + for (EPUserAppsManualSortPreference userAppPref : epManualApps) { + existingAppIds.put(userAppPref.getAppId(), userAppPref); + } + if (existingAppIds.containsKey(delAppSortManual.getAppId()) && !delAppSortManual.isSelect()) { + dataAccessService.deleteDomainObjects(EPUserAppsManualSortPreference.class, + "app_id=" + delAppSortManual.getAppId() + " AND user_id=" + user.getId(), null); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_OK); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "deleteUserAppSortManual failed", e); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + return fieldsValidator; + } + + @SuppressWarnings("unchecked") + @Override + public FieldsValidator saveAppsSortPreference(EPAppsSortPreference appsSortPreference, EPUser user) { + FieldsValidator fieldsValidator = new FieldsValidator(); + final Map params = new HashMap<>(); + List epSortTypes = new ArrayList(); + EPUserAppsSortPreference usrSortPr = null; + try { + params.put("userId", user.getId()); + epSortTypes = dataAccessService.executeNamedQuery("userAppsSortPreferenceQuery", params, null); + if (epSortTypes.size() == 0) { + usrSortPr = new EPUserAppsSortPreference(); + usrSortPr.setUserId(Ints.checkedCast(user.getId())); + usrSortPr.setSortPref(appsSortPreference.getValue()); + dataAccessService.saveDomainObject(usrSortPr, null); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_OK); + } else { + usrSortPr = epSortTypes.get(0); + usrSortPr.setSortPref(appsSortPreference.getValue()); + HashMap additionalUpdateParam = new HashMap(); + additionalUpdateParam.put("userId", usrSortPr.getUserId()); + dataAccessService.saveDomainObject(usrSortPr, additionalUpdateParam); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_OK); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "saveAppsSortPreference failed", e); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + return fieldsValidator; + } + + @SuppressWarnings("unchecked") + @Override + public String getUserAppsSortTypePreference(EPUser user) { + final Map params = new HashMap<>(); + List userSortPrefs = new ArrayList(); + try { + params.put("userId", user.getId()); + userSortPrefs = dataAccessService.executeNamedQuery("userAppsSortPreferenceQuery", params, null); + if (userSortPrefs.size() > 0) + return userSortPrefs.get(0).getSortPref(); + else + return null; + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getUserAppsSortTypePreference failed", e); + } + return null; + + } + + @Override + public List getUserRemoteApps(String id) { + throw new RuntimeException(" Cannot be called from parent class"); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAppService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAppService.java new file mode 100644 index 00000000..c7bf680d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAppService.java @@ -0,0 +1,213 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.AdminUserApplications; +import org.openecomp.portalapp.portal.domain.AppIdAndNameTransportModel; +import org.openecomp.portalapp.portal.domain.AppsResponse; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EcompApp; +import org.openecomp.portalapp.portal.domain.UserRoles; +import org.openecomp.portalapp.portal.ecomp.model.AppCatalogItem; +import org.openecomp.portalapp.portal.transport.EPAppsManualPreference; +import org.openecomp.portalapp.portal.transport.EPAppsSortPreference; +import org.openecomp.portalapp.portal.transport.EPDeleteAppsManualSortPref; +import org.openecomp.portalapp.portal.transport.EPWidgetsSortPreference; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.LocalRole; +import org.openecomp.portalapp.portal.transport.OnboardingApp; + +public interface EPAppService { + + /** + * Get all applications adminId is an admin + * + * @param adminId + * - the admin + * @return the admin's applications + */ + List getUserAsAdminApps(EPUser user); + + List getUserByOrgUserIdAsAdminApps(String orgUserId); + + /** + * Gets all rows and all fields from the fn_app table. + * + * @return list of EPApp objects + */ + List getAppsFullList(); + + /** + * Gets all rows and most fields from the fn_app table. + * + * @return list of EcompApp objects. + */ + List getEcompAppAppsFullList(); + + /** + * Get apps with app app admins + * + * @return List of AdminUserApplications + */ + List getAppsAdmins(); + + /** + * Get all apps from fn_app table (index, name, title only). + * + * @param all + * If all is true, returns active and inactive apps; otherwise, + * just active apps. + * @return List of AppsResponse objects. + */ + List getAllApps(Boolean all); + + UserRoles getUserProfile(String loginId); + + UserRoles getUserProfileNormalized(EPUser user); + + List getAppRoles(Long appId); + + List getAdminApps(EPUser user); + + List getAppsForSuperAdminAndAccountAdmin(EPUser user); + + /** + * Gets the applications accessible to the specified user, which includes + * all enabled open applications, plus all enabled applications for which + * the user has a defined role for that app. + * + * @param user + * EPUser object with the user's ATT UID + * @return the user's list of applications, which may be empty. + */ + List getUserApps(EPUser user); + + /** + * Gets the user-personalized list of applications for the Portal (super) + * admin, which includes enabled open applications, enabled applications for + * which the user has a defined role for that app, and/or enabled + * applications which the user has chosen to show. + * + * @param user + * EPUser object with the user's ATT UID + * @return the user's personalized list of applications, which may be empty. + */ + List getPersAdminApps(EPUser user); + + /** + * Gets the user-personalized list of accessible applications, which + * includes enabled open applications and/or enabled applications for which + * the user has a defined role for that app. Personalization means the user + * can indicate an accessible application should be excluded from this + * result. + * + * @param user + * EPUser object with the user's ATT UID + * @return the user's personalized list of applications, which may be empty. + */ + List getPersUserApps(EPUser user); + + /** + * Gets the application catalog for the specified user who is a super admin. + * This includes all enabled applications. Each item indicates whether the + * user has access (open or via a role), and whether the application is + * selected for showing in the user's home (applications) page. Admin sees + * slightly different behavior - can force an app onto the home page using + * the personalization feature (user-app-selection table). + * + * @param user + * @return list of all enabled applications, which may be empty + */ + List getAdminAppCatalog(EPUser user); + + /** + * Gets the application catalog for the specified user, who is a regular + * user. This includes all enabled applications. Each item indicates whether + * the user has access (open or via a role), and whether the application is + * selected for showing in the user's home (applications) page. + * + * @param user + * @return list of all enabled applications, which may be empty + */ + List getUserAppCatalog(EPUser user); + + List getOnboardingApps(); + + List getEnabledNonOpenOnboardingApps(); + + FieldsValidator modifyOnboardingApp(OnboardingApp modifiedOnboardingApp, EPUser user); + + FieldsValidator addOnboardingApp(OnboardingApp newOnboardingApp, EPUser user); + + /** + * Deletes the specified application from all tables where the app_id is + * used, and ultimately from the fn_app table. + * + * @param user + * Must be Portal (super) administrator + * @param onboardingAppId + * ID of application to be deleted + * @return Status code + */ + FieldsValidator deleteOnboardingApp(EPUser user, Long onboardingAppId); + + List transformAppsToEcompApps(List appsList); + + EPApp getApp(Long appId); + + EPApp getAppDetail(String appName); + + List getAppsOrderByName(EPUser user); + + FieldsValidator saveAppsSortPreference(EPAppsSortPreference appsSortPreference, EPUser user); + + FieldsValidator saveAppsSortManual(List appsSortManual, EPUser user); + + FieldsValidator saveWidgetsSortManual(List widgetsSortManual, EPUser user); + + /** + * Deletes the sort order of user apps by sort manual preference + * + * @param delAppSortManual + * User Apps Data + * @param user + * LoggedIn User Data + * @return FieldsValidator + */ + FieldsValidator deleteUserAppSortManual(EPDeleteAppsManualSortPref delAppSortManual, EPUser user); + + FieldsValidator deleteUserWidgetSortPref(List delWidgetSortPref, EPUser user); + + String getUserAppsSortTypePreference(EPUser user); + + List getAppsOrderByLastUsed(EPUser user); + + List getAppsOrderByMostUsed(EPUser user); + + List getAppsOrderByManual(EPUser user); + + List getUserRemoteApps(String id); + + void createOnboardingFromApp(EPApp app, OnboardingApp onboardingApp); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAuditService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAuditService.java new file mode 100644 index 00000000..a35a8ba2 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAuditService.java @@ -0,0 +1,27 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.Date; + +public interface EPAuditService { + public Date getGuestLastLogin(String userId); + public void delAuditLogFromDay(); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAuditServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAuditServiceImpl.java new file mode 100644 index 00000000..1a343866 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPAuditServiceImpl.java @@ -0,0 +1,96 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.time.LocalDate; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.domain.AuditLog; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; + +@Service("epAuditService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class EPAuditServiceImpl implements EPAuditService { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPAuditServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + + @Override + /* get the guest last login time with orgUserId as param. + * If record not found in table, return null. + * + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.EPUserService#getGuestLastLogin(java.lang.String) + */ + public Date getGuestLastLogin(String userId) { + Map params = new HashMap<>(); + params.put("userId", userId); + List list = getDataAccessService().executeNamedQuery("getGuestLastLogin", params, null); + Date date=null; + if(list!=null){ + if(list.size()==1) /* if list only contains one item, meaning this is the first time user logs in or record not found in db*/ + date = list.get(0); /*the guest's current log in time*/ + else if(list.size()==2) + date = list.get(1); /*most recent login date from db*/ + } + return date; + } + + @Override + /* Clean all the records in fn_audit_log table that are less than defined date in system.property + * + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.EPAuditService#delAuditLogFromDay() + */ + public void delAuditLogFromDay(){ + if (EPCommonSystemProperties.containsProperty(EPCommonSystemProperties.AUDITLOG_DEL_DAY_FROM)) { + String day = EPCommonSystemProperties.getProperty(EPCommonSystemProperties.AUDITLOG_DEL_DAY_FROM); + LocalDate removeDateFrom = LocalDate.now().minusDays(Integer.valueOf(day)); + getDataAccessService().deleteDomainObjects(AuditLog.class, "audit_date <'"+removeDateFrom+"'",null); + }else{ + logger.error(EELFLoggerDelegate.errorLogger, "delAuditLogFromDay Exception = system.propertiy value is empty on" + EPCommonSystemProperties.AUDITLOG_DEL_DAY_FROM); + } + } + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + + public void setDataAccessService(DataAccessService dataAccessService) { + this.dataAccessService = dataAccessService; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLdapService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLdapService.java new file mode 100644 index 00000000..c77684fa --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLdapService.java @@ -0,0 +1,31 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import org.openecomp.portalsdk.core.command.support.SearchResult; +import org.openecomp.portalsdk.core.domain.support.DomainVo; + + +public interface EPLdapService { + + // search POST for users based on the criteria selected in the Request + SearchResult searchPost(DomainVo searchCriteria, String sortBy1, String sortBy2, String sortBy3, int pageNo, int dataSize, int userId) throws Exception; + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLdapServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLdapServiceImpl.java new file mode 100644 index 00000000..dfcc0c7f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLdapServiceImpl.java @@ -0,0 +1,288 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; + +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.command.support.SearchResult; +import org.openecomp.portalsdk.core.domain.support.DomainVo; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.support.FusionService; +import org.openecomp.portalsdk.core.service.support.ServiceLocator; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; + +@Service("epLdapService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +public class EPLdapServiceImpl extends FusionService implements EPLdapService { + @Autowired + private ServiceLocator serviceLocator; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPLdapServiceImpl.class); + + @EPAuditLog + @SuppressWarnings({ "rawtypes", "unchecked" }) + public SearchResult searchPost(DomainVo searchCriteria, String sortBy1, String sortBy2, String sortBy3, + int pageNo, int dataSize, int userId) throws Exception { + + String remoteHost = ""; + + // initialize the directory context to access POST + DirContext dirContext = serviceLocator.getDirContext(SystemProperties.getProperty(SystemProperties.POST_INITIAL_CONTEXT_FACTORY), + SystemProperties.getProperty(SystemProperties.POST_PROVIDER_URL), + SystemProperties.getProperty(SystemProperties.POST_SECURITY_PRINCIPAL)); + + SearchResult searchResult = new SearchResult(); + + try { + + remoteHost = String.format("%s/%s", SystemProperties.getProperty(SystemProperties.POST_PROVIDER_URL), + SystemProperties.getProperty(SystemProperties.POST_SECURITY_PRINCIPAL)); + MDC.put(EPCommonSystemProperties.FULL_URL, remoteHost); + + String[] postAttributes = {"nickname","givenName","initials","sn","employeeNumber","mail","telephoneNumber", + "departmentNumber","a1","street","roomNumber","l","st","postalCode","zip4","physicalDeliveryOfficeName","bc", + "friendlyCountryName","bd","bdname","bu","buname","jtname","mgrid","a2","compcode","compdesc", + "costcenter","silo","b2"}; + + SearchControls searchControls = new SearchControls(); + searchControls.setTimeLimit(5000); + searchControls.setReturningAttributes(postAttributes); + + StringBuffer filterClause = new StringBuffer("(&(objectClass=*)"); + + EPUser user = (EPUser)searchCriteria; + + if(Utilities.nvl(user.getFirstName()).length() > 0) { + filterClause.append("(givenName=").append(user.getFirstName()).append("*)"); + } + if(Utilities.nvl(user.getLastName()).length() > 0) { + filterClause.append("(sn=").append(user.getLastName()).append("*)"); + } + if(Utilities.nvl(user.getHrid()).length() > 0) { + filterClause.append("(employeeNumber=").append(user.getHrid()).append("*)"); + } + if(Utilities.nvl(user.getOrgManagerUserId()).length() > 0) { + filterClause.append("(mgrid=").append(user.getOrgManagerUserId()).append("*)"); + } + if(Utilities.nvl(user.getOrgCode()).length() > 0) { + filterClause.append("(departmentNumber=").append(user.getOrgCode()).append("*)"); + } + if(Utilities.nvl(user.getEmail()).length() > 0) { + filterClause.append("(mail=").append(user.getEmail()).append("*)"); + } + if(Utilities.nvl(user.getOrgUserId()).length() > 0) { + filterClause.append("(a1=").append(user.getOrgUserId()).append("*)"); + } + filterClause.append("(c3=N)"); // this has been added to filter CP09 entries on the LDAP server that are duplicates of existing individuals + filterClause.append(")"); + + List list = new ArrayList(); + if (!filterClause.toString().equals("(&(objectClass=*))")) { + NamingEnumeration e = dirContext.search(SystemProperties.getProperty(SystemProperties.POST_PROVIDER_URL) + "/" + + SystemProperties.getProperty(SystemProperties.POST_SECURITY_PRINCIPAL), + filterClause.toString(), + searchControls); + list = processResults(e); + } + + Collections.sort(list); + + searchResult = new SearchResult(list); + searchResult.setPageNo(pageNo); + if(dataSize >= 0) { + searchResult.setDataSize(dataSize); + } + else { + searchResult.setDataSize(list.size()); + } + } catch(NamingException ne) { + String stackTrace = EcompPortalUtils.getStackTrace(ne); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while performing the LDAP search. Details: " + stackTrace); + } catch(Exception e) { + String stackTrace = EcompPortalUtils.getStackTrace(e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while performing the LDAP search. Details: " + stackTrace); + } + finally { + dirContext.close(); + } + + return searchResult; + } + + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @EPMetricsLog + private ArrayList processResults(NamingEnumeration e) throws NamingException { + ArrayList results = new ArrayList(); + int count = 0; + + while (e.hasMore()) { + javax.naming.directory.SearchResult searchResult = (javax.naming.directory.SearchResult)e.next(); + results.add(processAttributes(searchResult.getAttributes())); + count++; + + if(count > Integer.parseInt(SystemProperties.getProperty(SystemProperties.POST_MAX_RESULT_SIZE))) { + break; + } + } + return results; + } + + + @SuppressWarnings("rawtypes") + @EPMetricsLog + private DomainVo processAttributes(Attributes resultAttributes) throws NamingException { + EPUser user = new EPUser(); + + try { + if (resultAttributes == null) { + logger.debug(EELFLoggerDelegate.debugLogger, "This result has no attributes"); + } else { + for (NamingEnumeration e = resultAttributes.getAll(); e.hasMore();) { //why the nested loop? + Attribute attribute = (Attribute)e.next(); + for (NamingEnumeration ie = attribute.getAll(); ie.hasMore();) { + if (attribute.getID().equalsIgnoreCase("nickname")) { + user.setFirstName((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("initials")) { + user.setMiddleInitial((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("sn")) { + user.setLastName((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("employeeNumber")) { + user.setHrid((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("mail")) { + user.setEmail((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("telephoneNumber")) { + user.setPhone((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("departmentNumber")) { + user.setOrgCode((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("a1")) { + user.setOrgUserId((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("street")) { + user.setAddress1((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("roomNumber")) { + user.setAddress2((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("l")) { + user.setCity((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("st")) { + user.setState((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("postalCode")) { + user.setZipCode((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("zip4")) { + user.setZipCodeSuffix((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("physicalDeliveryOfficeName")) { + user.setLocationClli((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("bc")) { + user.setBusinessCountryCode((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("friendlyCountryName")) { + user.setBusinessCountryName((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("bd")) { + user.setDepartment((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("bdname")) { + user.setDepartmentName((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("jtname")) { + user.setJobTitle((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("mgrid")) { + user.setOrgManagerUserId((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("a2")) { + user.setCommandChain((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("compcode")) { + user.setCompanyCode((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("compdesc")) { + user.setCompany((String) ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("bu")) { + user.setBusinessUnit((String)ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("buname")) { + user.setBusinessUnitName((String)ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("silo")) { + user.setSiloStatus((String)ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("costcenter")) { + user.setCostCenter((String)ie.next()); + } + else if (attribute.getID().equalsIgnoreCase("b2")) { + user.setFinancialLocCode((String)ie.next()); + } + else { //we don't care about returned attribute, let's move on + ie.next(); + } + + } + } + } + } catch (NamingException e) { + String stackTrace = EcompPortalUtils.getStackTrace(e); + logger.error(EELFLoggerDelegate.errorLogger, "An error occurred while processing the following user from POST with an Organization User ID of " + user.getOrgUserId()); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occcurred while processing LDAP search results, Details: " + stackTrace); + } catch(Exception e) { + String stackTrace = EcompPortalUtils.getStackTrace(e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occcurred while processing LDAP search results, Details: " + stackTrace); + } + + return user; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLeftMenuService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLeftMenuService.java new file mode 100644 index 00000000..ed9c3c4d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLeftMenuService.java @@ -0,0 +1,44 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.Set; + +import org.openecomp.portalsdk.core.domain.MenuData; +import org.openecomp.portalapp.portal.domain.EPUser; + +public interface EPLeftMenuService { + + /** + * Builds a JSON suitable for populating the front-end left menu from the supplied set of menu items. + * @param user + * + * @param fullMenuSet + * @param roleFunctionSet + * @return JSON String of this form: + *
+	 * {"navItems":[
+	 * 		{"name":"Home","imageSrc":"icon-location-pin","state":"root.applicationsHome"},
+	 * 	...
+	 * ] }
+	 * 
+ */ + String getLeftMenuItems(EPUser user, Set fullMenuSet, Set roleFunctionSet); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLeftMenuServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLeftMenuServiceImpl.java new file mode 100644 index 00000000..2a1f466a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLeftMenuServiceImpl.java @@ -0,0 +1,195 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.Collection; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.domain.MenuData; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.UserRoles; +//import org.openecomp.portalapp.portal.domain.Menu; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; + +@Service("leftMenuService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog + +public class EPLeftMenuServiceImpl implements EPLeftMenuService { + + private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPLeftMenuServiceImpl.class); + + @Autowired + private EPAppService appService; + + /* + * (non-Javadoc) + * + * @see + * org.openecomp.portalapp.portal.service.EPLeftMenuService#getLeftMenuItems + * (java.util.Set) + */ + @Override + public String getLeftMenuItems(EPUser user, Set fullMenuSet, Set roleFunctionSet) { + final Map defaultNavMap = new LinkedHashMap(); + + resetNavMap(defaultNavMap); + + loadDefaultNavMap(defaultNavMap); + + // Handle Account Administrator in a special way; soon this will + // be revised as Account Administrator may become obsolete + try { + if (user != null) { + UserRoles uRoles = appService.getUserProfileNormalized(user); + if (uRoles.getRoles().contains("Account Administrator")) + loadAccAdminNavMap(defaultNavMap); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "getLeftMenuItems: failed to get roles for user " + user.getOrgUserId(), e); + } + + loadNavMapByRole(defaultNavMap, fullMenuSet); + + return convertToSideBarModel(defaultNavMap); + } + + /** + * Clears the map + * + * @param defaultNavMap + */ + private void resetNavMap(Map defaultNavMap) { + defaultNavMap.clear(); + } + + /** + * + * @param defaultNavMap + * @param fullMenuSet + */ + private void loadNavMapByRole(Map defaultNavMap, Set fullMenuSet) { + + class SortOrderComparator implements Comparator { + @Override + public int compare(MenuData e1, MenuData e2) { + return e1.getSortOrder().compareTo(e2.getSortOrder()); + } + } + + SortedSet sortMenuSet = new TreeSet(new SortOrderComparator()); + for (MenuData mn : fullMenuSet) { + sortMenuSet.add(mn); + } + + for (MenuData mn : sortMenuSet) { + JSONObject navItemsDetails = new JSONObject(); + navItemsDetails.put("name", mn.getLabel()); + navItemsDetails.put("state", mn.getAction()); + navItemsDetails.put("imageSrc", mn.getImageSrc()); + defaultNavMap.put(mn.getAction(), navItemsDetails); + } + } + + /** + * + * @param defaultNavMap + * @return + */ + private String convertToSideBarModel(Map defaultNavMap) { + JSONObject sidebarModel = new JSONObject(); + JSONArray navItems = new JSONArray(); + Collection jsonObjs = defaultNavMap.values(); + + for (JSONObject navItemsDetail : jsonObjs) + navItems.put(navItemsDetail); + + sidebarModel.put("label", "ECOMP portal"); + sidebarModel.put("navItems", navItems); + return sidebarModel.toString(); + } + + /** + * Loads default entries for regular user. + * + * @param defaultNavMap + */ + private void loadDefaultNavMap(Map defaultNavMap) { + + JSONObject navItemsDetails1 = new JSONObject(); + navItemsDetails1.put("name", "Home"); + navItemsDetails1.put("state", "root.applicationsHome"); + navItemsDetails1.put("imageSrc", "icon-building-home"); + defaultNavMap.put("root.applicationsHome", navItemsDetails1); + + JSONObject navItemsDetails2 = new JSONObject(); + navItemsDetails2.put("name", "Application Catalog"); + navItemsDetails2.put("state", "root.appCatalog"); + navItemsDetails2.put("imageSrc", "icon-apps-marketplace"); + defaultNavMap.put("root.appCatalog", navItemsDetails2); + + JSONObject navItemsDetails3 = new JSONObject(); + navItemsDetails3.put("name", "Widget Catalog"); + navItemsDetails3.put("state", "root.widgetCatalog"); + navItemsDetails3.put("imageSrc", "icon-apps-marketplace"); + defaultNavMap.put("root.widgetCatalog", navItemsDetails3); + + } + + /** + * Loads default entries for application administrator. + * + * @param defaultNavMap + */ + private void loadAccAdminNavMap(Map defaultNavMap) { + + JSONObject navItemsDetails1 = new JSONObject(); + navItemsDetails1.put("name", "Users"); + navItemsDetails1.put("state", "root.users"); + navItemsDetails1.put("imageSrc", "icon-user"); + defaultNavMap.put("root.users", navItemsDetails1); + + // No more widget onboarding like this: + // + // JSONObject navItemsDetails2 = new JSONObject(); + // navItemsDetails2.put("name", "Widget Onboarding"); + // navItemsDetails2.put("state", "root.widgetOnboarding"); + // navItemsDetails2.put("imageSrc", "icon-add-widget"); + // defaultNavMap.put("root.widgetOnboarding", navItemsDetails2); + + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLoginService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLoginService.java new file mode 100644 index 00000000..4bdb2009 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLoginService.java @@ -0,0 +1,70 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.HashMap; + +import org.openecomp.portalapp.command.EPLoginBean; +import org.openecomp.portalapp.portal.domain.EPUser; + +public interface EPLoginService { + + /** + * Calls {@link #findUser(EPLoginBean, String, HashMap, boolean)} with the + * last parameter set to true. + * + * @param bean + * @param menuPropertiesFilename + * @param additionalParams + * @return EPUser object; null on error or if no match. + * @throws Exception + */ + @SuppressWarnings("rawtypes") + EPLoginBean findUser(EPLoginBean bean, String menuPropertiesFilename, HashMap additionalParams) throws Exception; + + /** + * Searches the fn_user table for a row that matches information in the + * bean. Uses the bean's Organization User ID property if present; if not, uses the bean's + * LoginId property and (optionally, depending on matchPassword parameter) + * loginPwd property. + * + * @param bean + * EPLoginBean + * @param menuPropertiesFileName + * Always ignored + * @param additionalParams + * Used by DataAccessService when updating a matched user object + * @param matchPassword + * If true, the search must match the password + * @return EPUser object; null on error or if no match. + */ + @SuppressWarnings("rawtypes") + EPLoginBean findUser(EPLoginBean bean, String menuPropertiesFilename, HashMap additionalParams, + boolean matchPassword) throws Exception; + + /** + * Searches the fn_user table for a row with a value in column login_id that + * matches the specified value. + * + * @param loginId + * @return EPUser object; null on error or if no match. + */ + public EPUser findUserWithoutPwd(String loginId); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLoginServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLoginServiceImpl.java new file mode 100644 index 00000000..ba202d63 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPLoginServiceImpl.java @@ -0,0 +1,234 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.menu.MenuBuilder; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.service.support.FusionService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalsdk.core.web.support.AppUtils; +import org.openecomp.portalapp.command.EPLoginBean; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.util.EPUserUtils; + +@Service("eploginService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class EPLoginServiceImpl extends FusionService implements EPLoginService { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPLoginServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.EPLoginService#findUser(org.openecomp.portalapp.command.EPLoginBean, java.lang.String, java.util.HashMap) + */ + @SuppressWarnings("rawtypes") + public EPLoginBean findUser(EPLoginBean bean, String menuPropertiesFilename, HashMap additionalParams) + throws Exception { + return findUser(bean, menuPropertiesFilename, additionalParams, true); + } + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.EPLoginService#findUser(org.openecomp.portalapp.command.EPLoginBean, java.lang.String, java.util.HashMap, boolean) + */ + @SuppressWarnings("rawtypes") + public EPLoginBean findUser(EPLoginBean bean, String menuPropertiesFilename_ignored, HashMap additionalParams, + boolean matchPassword) throws Exception { + EPUser user = null; + EPUser userCopy = null; + + if (bean.getOrgUserId() != null) { + user = (EPUser) findUser(bean); + } else { + if (matchPassword) + user = (EPUser) findUser(bean.getLoginId(), bean.getLoginPwd()); + else + user = (EPUser) findUserWithoutPwd(bean.getLoginId()); + } + + // run this command to fetch more information from the lazily loaded + // object + + // This is funny - commenting out the following method call + // 1. What are we doing with the returned values of the following two + // methods? Nothing. + // 2. Use a guest user scenario - user object will be null - clealry, + // NPE. + // 3. A check of if(user !=null) is made AFTER these bogus calls :) - If + // these calls WERE doing anything significat (which they are not), + // shouln't they have been moved inside that if check? + + // user.getEPUserApps(); + + // Comments + // 1. This method is clearly doing more than 'getting roles' - Not a + // good name - + // 2. Also, there is no null check - guest user scenarios will break the + // code with NPE - added the check - Do not want to remove the call + // altogether - not sure how it will effect things. + + if (user != null) { + user.getEPRoles(); + + // raise an error if the portal application is locked and the user + // does not + // have system administrator privileges + if (AppUtils.isApplicationLocked() + && !EPUserUtils.hasRole(user, SystemProperties.getProperty(SystemProperties.SYS_ADMIN_ROLE_ID))) { + bean.setLoginErrorMessage(SystemProperties.MESSAGE_KEY_LOGIN_ERROR_APPLICATION_LOCKED); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUserAdminPrivilegesInfo, user.getLoginId()); + } + + // raise an error if the user is inactive + if (!user.getActive()) { + bean.setLoginErrorMessage(SystemProperties.MESSAGE_KEY_LOGIN_ERROR_USER_INACTIVE); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUserInactiveWarning, user.getLoginId()); + } + + // only login the user if no errors have occurred + if (bean.getLoginErrorMessage() == null) { + + // this will be a snapshot of the user's information as + // retrieved from the database + userCopy = (EPUser) user.clone(); + + // update the last logged in date for the user + user.setLastLoginDate(new Date()); + getDataAccessService().saveDomainObject(user, additionalParams); + + // create the application menu based on the user's privileges + MenuBuilder menuBuilder = new MenuBuilder(); + Set appMenu = menuBuilder.getMenu( + SystemProperties.getProperty(SystemProperties.APPLICATION_MENU_SET_NAME), dataAccessService); + bean.setMenu(appMenu != null ? appMenu : new HashSet()); + Set businessDirectMenu = menuBuilder.getMenu( + SystemProperties.getProperty(SystemProperties.BUSINESS_DIRECT_MENU_SET_NAME), + dataAccessService); + bean.setBusinessDirectMenu(businessDirectMenu != null ? businessDirectMenu : new HashSet()); + + bean.setUser(userCopy); + } + + } else { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUserMissingError, bean.getOrgUserId()); + } + + return bean; + } + + /** + * Searches the fn_user table for a row that matches the specified login_id + * and login_pwd values. + * + * @param loginId + * @param password + * @return EPUser object; null on error or if no match. + */ + private EPUser findUser(String loginId, String password) { + List list = null; + + StringBuffer criteria = new StringBuffer(); + criteria.append(" where login_id = '").append(loginId).append("'").append(" and login_pwd = '").append(password) + .append("'"); + + try { + list = getDataAccessService().getList(EPUser.class, criteria.toString(), null, null); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + logger.error(EELFLoggerDelegate.errorLogger, "findUser(String) failed on " + loginId, e); + } + + return (list == null || list.size() == 0) ? null : (EPUser) list.get(0); + } + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.EPLoginService#findUserWithoutPwd(java.lang.String) + */ + @Override + public EPUser findUserWithoutPwd(String loginId) { + List list = null; + + StringBuffer criteria = new StringBuffer(); + criteria.append(" where login_id = '").append(loginId).append("'"); + + try { + list = getDataAccessService().getList(EPUser.class, criteria.toString(), null, null); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + String message = "findUserWithoutPwd failed on " + loginId; + logger.error(EELFLoggerDelegate.errorLogger, message, e); + } + + return (list == null || list.size() == 0) ? null : (EPUser) list.get(0); + } + + /** + * Searches the fn_user table for a row that matches the value of the bean's + * Organization User ID property. + * + * @param bean + * @return EPUser object; null on error or if no match. + */ + private EPUser findUser(EPLoginBean bean) { + List list = null; + + StringBuffer criteria = new StringBuffer(); + criteria.append(" where orgUserId = '").append(bean.getOrgUserId()).append("'"); + + try { + list = getDataAccessService().getList(EPUser.class, criteria.toString(), null, null); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + logger.error(EELFLoggerDelegate.errorLogger, "findUser(EPLoginBean) failed", e); + } + + return (list == null || list.size() == 0) ? null : (EPUser) list.get(0); + } + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + + public void setDataAccessService(DataAccessService dataAccessService) { + this.dataAccessService = dataAccessService; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPRoleService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPRoleService.java new file mode 100644 index 00000000..32dccbe0 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPRoleService.java @@ -0,0 +1,37 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalsdk.core.domain.RoleFunction; +import org.openecomp.portalapp.portal.domain.EPRole; + + +public interface EPRoleService { + + // Used by ECOMP. Get cached role by two columns used by ECOMP only. app id, and external app role id. + EPRole getRole(Long appId, Long appRoleid); + public void saveRole(EPRole domainRole); + EPRole getAppRole(String roleName, Long appId); + public List getRoleFunctions(); + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPRoleServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPRoleServiceImpl.java new file mode 100644 index 00000000..d7661e9a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/EPRoleServiceImpl.java @@ -0,0 +1,171 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.domain.RoleFunction; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalapp.portal.domain.EPRole; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; + +@Service("epRoleService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class EPRoleServiceImpl implements EPRoleService{ + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPRoleServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + + @SuppressWarnings("unchecked") + public List getRoleFunctions() { + //List msgDB = getDataAccessService().getList(Profile.class, null); + return getDataAccessService().getList(RoleFunction.class, null); + } + + @SuppressWarnings("unchecked") + public List getAvailableChildRoles(Long roleId) { + List availableChildRoles = (List)getDataAccessService().getList(EPRole.class,null); + if(roleId==null || roleId==0){ + return availableChildRoles; + } + + EPRole currentRole = (EPRole)getDataAccessService().getDomainObject(EPRole.class,roleId,null); + Set allParentRoles = new TreeSet(); + allParentRoles = getAllParentRolesAsList(currentRole, allParentRoles); + + + Iterator availableChildRolesIterator = availableChildRoles.iterator(); + while (availableChildRolesIterator.hasNext()) { + EPRole role = availableChildRolesIterator.next(); + if(!role.getActive() || allParentRoles.contains(role) || role.getId().equals(roleId)){ + availableChildRolesIterator.remove(); + } + } + return availableChildRoles; + } + + private Set getAllParentRolesAsList(EPRole role, Set allParentRoles) { + Set parentRoles = role.getParentRoles(); + allParentRoles.addAll(parentRoles); + Iterator parentRolesIterator = parentRoles.iterator(); + while (parentRolesIterator.hasNext()) { + getAllParentRolesAsList(parentRolesIterator.next(),allParentRoles); + } + return allParentRoles; + } + + public RoleFunction getRoleFunction(String code) { + return (RoleFunction)getDataAccessService().getDomainObject(RoleFunction.class, code, null); + } + + public void saveRoleFunction(RoleFunction domainRoleFunction) { + getDataAccessService().saveDomainObject(domainRoleFunction, null); + } + + public void deleteRoleFunction(RoleFunction domainRoleFunction) { + getDataAccessService().deleteDomainObject(domainRoleFunction, null); + } + + public EPRole getRole(Long id) { + return (EPRole)getDataAccessService().getDomainObject(EPRole.class, id, null); + } + + // TODO: refactor + private static final String getAppRoleSqlFormat ="SELECT * FROM fn_role where APP_ID = %s AND APP_ROLE_ID = %s"; + + @SuppressWarnings("unchecked") + public EPRole getRole(Long appId, Long appRoleid) { + if(appId == null || appRoleid == null){ + logger.error(EELFLoggerDelegate.errorLogger, String.format("getRole does not support null appId or roleId. appRoleid=%s, appRoleid=%s", appId, appRoleid)); + return null; + } + + String sql = String.format(getAppRoleSqlFormat, appId, appRoleid); + + List roles = (List)dataAccessService.executeSQLQuery(sql, EPRole.class, null); + int resultsCount = roles.size(); + if(resultsCount > 1){ + logger.error(EELFLoggerDelegate.errorLogger, String.format("search by appId=%s, appRoleid=%s should have returned 0 or 1 results. Got %d. This is an internal server error.", appId, appRoleid, resultsCount)); + logger.error(EELFLoggerDelegate.errorLogger, "Trying to recover from duplicates by returning the first search result. This issue should be treated, it is probably not critical because duplicate roles should be similar."); + return roles.get(0); + } else if(resultsCount == 1){ + return roles.get(0); + } + return null; + } + + @SuppressWarnings("unchecked") + public EPRole getAppRole(String roleName, Long appId) { + + final Map params = new HashMap(); + params.put("appId", appId.toString()); + params.put("roleName", roleName); + + List roles = (List)dataAccessService.executeNamedQuery("getAppRoles", params, null); + int resultsCount = roles.size(); + if(resultsCount > 1){ + logger.error(EELFLoggerDelegate.errorLogger, "Trying to recover from duplicates by returning the first search result. This issue should be treated, it is probably not critical because duplicate roles should be similar."); + return roles.get(0); + } else if(resultsCount == 1){ + return roles.get(0); + } + return null; + } + + + public void saveRole(EPRole domainRole) { + getDataAccessService().saveDomainObject(domainRole, null); + } + + public void deleteRole(EPRole domainRole) { + getDataAccessService().deleteDomainObject(domainRole, null); + } + + @SuppressWarnings("unchecked") + public List getAvailableRoles() { + return getDataAccessService().getList(EPRole.class, null); + } + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + + + public void setDataAccessService(DataAccessService dataAccessService) { + this.dataAccessService = dataAccessService; + } +} + + diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/FunctionalMenuService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/FunctionalMenuService.java new file mode 100644 index 00000000..0fa790c5 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/FunctionalMenuService.java @@ -0,0 +1,58 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.FunctionalMenuItemWithAppID; +import org.openecomp.portalapp.portal.transport.BusinessCardApplicationRole; +import org.openecomp.portalapp.portal.transport.FavoritesFunctionalMenuItem; +import org.openecomp.portalapp.portal.transport.FavoritesFunctionalMenuItemJson; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.FunctionalMenuItem; +import org.openecomp.portalapp.portal.transport.FunctionalMenuItemWithRoles; +import org.openecomp.portalapp.portal.transport.FunctionalMenuRole; + +public interface FunctionalMenuService { + List getFunctionalMenuItems (EPUser user); + // return all active menu items + List getFunctionalMenuItems (); + // return all active menu items. If all is true, return all active and inactive menu items. + List getFunctionalMenuItems(Boolean all); + // return all active Functional menu items for Notification Tree in User Notification . If all is true, return all active menu items. + List getFunctionalMenuItemsForNotificationTree(Boolean all); + List getFunctionalMenuItemsForApp (Integer appId); + List getFunctionalMenuItemsForUser (String orgUserId); + FunctionalMenuItem getFunctionalMenuItemDetails (Integer menuid); + FieldsValidator createFunctionalMenuItem (FunctionalMenuItemWithRoles menuItemJson); + FieldsValidator editFunctionalMenuItem (FunctionalMenuItemWithRoles menuItemJson); + FieldsValidator deleteFunctionalMenuItem (Long menuId); + FieldsValidator regenerateAncestorTable (); + //Methods relevant to favorites + FieldsValidator setFavoriteItem(FavoritesFunctionalMenuItem menuItemJson); + List getFavoriteItems(Long userId); + FieldsValidator removeFavoriteItem (Long userId, Long menuId); + List transformFunctionalMenuItemWithAppIDToFunctionalMenuItem(List functionalMenuItemWithAppIDList); + List getFunctionalMenuRole(); + //Assign URLs under Help Menu + void assignHelpURLs(List menuItems); + List getUserAppRolesList(String userId); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/FunctionalMenuServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/FunctionalMenuServiceImpl.java new file mode 100644 index 00000000..a6a11209 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/FunctionalMenuServiceImpl.java @@ -0,0 +1,963 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.hibernate.Criteria; +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.criterion.Projections; +import org.hibernate.criterion.Restrictions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.FunctionalMenuItemWithAppID; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.transport.BusinessCardApplicationRole; +import org.openecomp.portalapp.portal.transport.FavoritesFunctionalMenuItem; +import org.openecomp.portalapp.portal.transport.FavoritesFunctionalMenuItemJson; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.FunctionalMenuItem; +import org.openecomp.portalapp.portal.transport.FunctionalMenuItemWithRoles; +import org.openecomp.portalapp.portal.transport.FunctionalMenuRole; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; + +@Service("functionalMenuService") +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class FunctionalMenuServiceImpl implements FunctionalMenuService { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(FunctionalMenuServiceImpl.class); + + private Long ACCOUNT_ADMIN_ROLE_ID = 999L; + private String RESTRICTED_APP_ROLE_ID = "900"; + + @Autowired + private DataAccessService dataAccessService; + @Autowired + private SessionFactory sessionFactory; + + @PostConstruct + private void init() { + try { + ACCOUNT_ADMIN_ROLE_ID = Long.valueOf(SystemProperties.getProperty(EPCommonSystemProperties.ACCOUNT_ADMIN_ROLE_ID)); + RESTRICTED_APP_ROLE_ID = SystemProperties.getProperty(EPCommonSystemProperties.RESTRICTED_APP_ROLE_ID); + } catch(Exception e) { + } + } + + public List getFunctionalMenuItems(EPUser user) { + List menuItems = new ArrayList(); + return menuItems; + } + + public List getFunctionalMenuItems() { + return getFunctionalMenuItems(false); + } + + public List getFunctionalMenuItems(Boolean all) { + // Divide this into 2 queries: one which returns the bottom-level menu items associated with Restricted apps, + // and one that returns all the other menu items. Then we can easily add the boolean flag + // restrictedApp to each FunctionalMenuItem, to be used by the front end. + String activeWhereClause = ""; + if (! all) { + activeWhereClause = " AND UPPER(m.active_yn) = 'Y' "; + } + String sql = "SELECT m.menu_id, m.column_num, m.text, m.parent_menu_id, m.url, m.active_yn, r.app_id " + + "FROM fn_menu_functional m, fn_menu_functional_roles r " + + "WHERE m.menu_id = r.menu_id " + + activeWhereClause //" AND UPPER(m.active_yn) = 'Y' " + + " AND r.role_id != '" + RESTRICTED_APP_ROLE_ID + "' " + + " UNION " + + " SELECT m.menu_id, m.column_num, m.text, m.parent_menu_id, m.url, m.active_yn,-1 app_id " + + " FROM fn_menu_functional m " + + " WHERE m.url='' " + + activeWhereClause; //" AND UPPER(m.active_yn) = 'Y' "; + logQuery(sql); + + @SuppressWarnings("unchecked") + List menuItemsWithAppIdList = dataAccessService.executeSQLQuery(sql,FunctionalMenuItemWithAppID.class, null); + List menuItems = new ArrayList<>(); + menuItems = transformFunctionalMenuItemWithAppIDToFunctionalMenuItem(menuItemsWithAppIdList); + for (FunctionalMenuItem menuItem : menuItems) { + menuItem.restrictedApp = false; + } + + sql = "SELECT m.menu_id, m.column_num, m.text, m.parent_menu_id, m.url, m.active_yn, r.app_id " + + "FROM fn_menu_functional m, fn_menu_functional_roles r " + + "WHERE m.menu_id = r.menu_id " + + activeWhereClause //" AND UPPER(m.active_yn) = 'Y' " + + " AND r.role_id = '" + RESTRICTED_APP_ROLE_ID + "' "; + logQuery(sql); + @SuppressWarnings("unchecked") + List menuItems2 = dataAccessService.executeSQLQuery(sql, FunctionalMenuItem.class, null); + for (FunctionalMenuItem menuItem : menuItems2) { + menuItem.restrictedApp = true; + menuItems.add(menuItem); + } + + return menuItems; + } + + + public List getFunctionalMenuItemsForNotificationTree(Boolean all) { + // Divide this into 2 queries: one which returns the bottom-level menu items associated with Restricted apps, + // and one that returns all the other menu items which are active. Then we can easily add the boolean flag + // restrictedApp to each FunctionalMenuItem, to be used by the front end. + String activeWhereClause = ""; + if (! all) { + activeWhereClause = " AND UPPER(m.active_yn) = 'Y' "; + } + String sql = "SELECT m.menu_id, m.column_num, m.text, m.parent_menu_id, m.url, m.active_yn, r.app_id " + + "FROM fn_menu_functional m, fn_menu_functional_roles r " + + "WHERE m.menu_id = r.menu_id " + + activeWhereClause + " AND UPPER(m.active_yn) = 'Y' " + + " AND r.role_id != '" + RESTRICTED_APP_ROLE_ID + "' " + + " UNION " + + " SELECT m.menu_id, m.column_num, m.text, m.parent_menu_id, m.url, m.active_yn,-1 app_id " + + " FROM fn_menu_functional m " + + " WHERE m.url='' " + + activeWhereClause + " AND UPPER(m.active_yn) = 'Y' "; + logQuery(sql); + + @SuppressWarnings("unchecked") + List menuItemsWithAppIdList = dataAccessService.executeSQLQuery(sql,FunctionalMenuItemWithAppID.class, null); + List menuItems = new ArrayList<>(); + menuItems = transformFunctionalMenuItemWithAppIDToFunctionalMenuItem(menuItemsWithAppIdList); + for (FunctionalMenuItem menuItem : menuItems) { + menuItem.restrictedApp = false; + } + + sql = "SELECT m.menu_id, m.column_num, m.text, m.parent_menu_id, m.url, m.active_yn, r.app_id " + + "FROM fn_menu_functional m, fn_menu_functional_roles r " + + "WHERE m.menu_id = r.menu_id " + + activeWhereClause + " AND UPPER(m.active_yn) = 'Y' " + + " AND r.role_id = '" + RESTRICTED_APP_ROLE_ID + "' "; + logQuery(sql); + @SuppressWarnings("unchecked") + List menuItems2 = dataAccessService.executeSQLQuery(sql, FunctionalMenuItem.class, null); + for (FunctionalMenuItem menuItem : menuItems2) { + menuItem.restrictedApp = true; + menuItems.add(menuItem); + } + + return menuItems; + } + + public List getFunctionalMenuItemsForApp(Integer appId) { + String sql = "SELECT DISTINCT m1.menu_id, m1.column_num, m1.text, m1.parent_menu_id, m1.url, m.active_yn " + + " FROM fn_menu_functional m, fn_menu_functional m1, fn_menu_functional_ancestors a, fn_menu_functional_roles mr " + + " WHERE " + " mr.app_id='" + appId + "' " + " AND mr.menu_id = m.menu_id " + " AND UPPER(m.active_yn) = 'Y'" + + " AND UPPER(m1.active_yn) ='Y'" + " AND a.menu_id = m.menu_id " + " AND a.ancestor_menu_id = m1.menu_id"; + logQuery(sql); + logger.debug(EELFLoggerDelegate.debugLogger, "getFunctionalMenuItemsForApp: logged the query"); + + @SuppressWarnings("unchecked") + List menuItems = dataAccessService.executeSQLQuery(sql, FunctionalMenuItem.class, null); + + return menuItems; + } + /** + * convert List of FunctionalMenuItemWithAppID into List of FunctionalMenuItem + * + */ + public List transformFunctionalMenuItemWithAppIDToFunctionalMenuItem(List functionalMenuItemWithAppIDList) { + List functionalMenuItemList = new ArrayList(); + for (FunctionalMenuItemWithAppID functionalMenuItemWithAppID : functionalMenuItemWithAppIDList) { + FunctionalMenuItem menuItem = new FunctionalMenuItem(); + menuItem.menuId=functionalMenuItemWithAppID.menuId; + menuItem.column=functionalMenuItemWithAppID.column; + menuItem.text=functionalMenuItemWithAppID.text; + menuItem.parentMenuId=functionalMenuItemWithAppID.parentMenuId; + menuItem.url=functionalMenuItemWithAppID.url; + menuItem.active_yn=functionalMenuItemWithAppID.active_yn; + menuItem.appid=functionalMenuItemWithAppID.appid; + menuItem.setRoles(functionalMenuItemWithAppID.roles); + menuItem.restrictedApp=functionalMenuItemWithAppID.restrictedApp; + functionalMenuItemList.add(menuItem); + } + return functionalMenuItemList; + } + + public List getFunctionalMenuItemsForUser(String orgUserId) { + // m represents the functional menu items that are the leaf nodes + // m1 represents the functional menu items for all the nodes + + // Divide this into 2 queries: one which returns the bottom-level menu items associated with Restricted apps, + // and one that returns all the other menu items. Then we can easily add the boolean flag + // restrictedApp to each FunctionalMenuItem, to be used by the front end. + String sql = "SELECT DISTINCT m1.menu_id, m1.column_num, m1.text, m1.parent_menu_id, m1.url, m.active_yn " + + " FROM fn_menu_functional m, fn_menu_functional m1, fn_menu_functional_ancestors a, " + + " fn_menu_functional_roles mr, fn_user u , fn_user_role ur " + " WHERE " + " u.org_user_id='" + orgUserId + + "' " + " AND u.user_id = ur.user_id " + " AND ur.app_id = mr.app_id " + + // " AND ur.role_id = mr.role_id " + + " AND (ur.role_id = mr.role_id " + " OR ur.role_id = '" + ACCOUNT_ADMIN_ROLE_ID + "') " + + " AND m.menu_id = mr.menu_id " + " AND UPPER(m.active_yn) = 'Y'" + " AND UPPER(m1.active_yn) ='Y' " + + " AND a.menu_id = m.menu_id " + " AND a.ancestor_menu_id = m1.menu_id " + + " UNION " + // the ancestors of the restricted app menu items + + " select m1.menu_id, m1.column_num, m1.text, m1.parent_menu_id, m1.url, m1.active_yn " + + " FROM fn_menu_functional m, fn_menu_functional_roles mr, fn_menu_functional m1, " + + " fn_menu_functional_ancestors a " + + " where a.menu_id = m.menu_id " + + " AND a.ancestor_menu_id = m1.menu_id " + + " AND m.menu_id != m1.menu_id " + + " AND m.menu_id = mr.menu_id " + + " AND mr.role_id = '" + RESTRICTED_APP_ROLE_ID + "' " + + " AND UPPER(m.active_yn) = 'Y'" + " AND UPPER(m1.active_yn) ='Y' " + // Add the Favorites menu item + + " UNION " + + " SELECT m.menu_id, m.column_num, m.text, m.parent_menu_id, m.url, m.active_yn " + + " FROM fn_menu_functional m " + + " WHERE m.text in ('Favorites','Get Access','Contact Us','Support','User Guide','Help')"; + + logQuery(sql); + logger.debug(EELFLoggerDelegate.debugLogger, "getFunctionalMenuItemsForUser: logged the query"); + + @SuppressWarnings("unchecked") + List menuItems = dataAccessService.executeSQLQuery(sql, FunctionalMenuItem.class, null); + for (FunctionalMenuItem menuItem : menuItems) { + menuItem.restrictedApp = false; + } + + sql = " SELECT m.menu_id, m.column_num, m.text, m.parent_menu_id, m.url, m.active_yn " + + " FROM fn_menu_functional m, fn_menu_functional_roles r " + + " WHERE m.menu_id = r.menu_id " + + " AND UPPER(m.active_yn) = 'Y' " + + " AND r.role_id = '" + RESTRICTED_APP_ROLE_ID + "' "; + logQuery(sql); + @SuppressWarnings("unchecked") + List menuItems2 = dataAccessService.executeSQLQuery(sql, FunctionalMenuItem.class, null); + for (FunctionalMenuItem menuItem : menuItems2) { + menuItem.restrictedApp = true; + menuItems.add(menuItem); + } + + return menuItems; + } + + public FunctionalMenuItem getFunctionalMenuItemDetails(Integer menuid) { + // First, fill in the fields that apply to all menu items + + String sql = "SELECT * FROM fn_menu_functional WHERE menu_id = '" + menuid + "'"; + logQuery(sql); + @SuppressWarnings("unchecked") + List menuItems = dataAccessService.executeSQLQuery(sql, FunctionalMenuItem.class, null); + FunctionalMenuItem menuItem = (menuItems == null || menuItems.isEmpty() ? null : menuItems.get(0)); + // If it is a bottom-level menu item, must fill in the appid and the + // roles + sql = "SELECT * FROM fn_menu_functional_roles WHERE menu_id = '" + menuid + "'"; + logQuery(sql); + @SuppressWarnings("unchecked") + List roleItems = dataAccessService.executeSQLQuery(sql, FunctionalMenuRole.class, null); + if (roleItems.size() > 0) { + Integer appid = roleItems.get(0).appId; + menuItem.appid = appid; + List roles = new ArrayList(); + for (FunctionalMenuRole roleItem : roleItems) { + logger.debug(EELFLoggerDelegate.debugLogger, "LR: app_id: " + roleItem.appId + "; role_id: " + roleItem.roleId + "\n"); + roles.add(roleItem.roleId); + } + menuItem.setRoles(roles); + } + + return menuItem; + } + + private FieldsValidator menuItemFieldsChecker(FunctionalMenuItemWithRoles menuItemJson) { + FieldsValidator fieldsValidator = new FieldsValidator(); + try { + // TODO: validate all the fields + @SuppressWarnings("unchecked") + List functionalMenuItems = dataAccessService.getList(FunctionalMenuItem.class, + " where text = '" + menuItemJson.text + "'", null, null); + + boolean dublicatedName = false; + for (FunctionalMenuItem fnMenuItem : functionalMenuItems) { + if (menuItemJson.menuId != null && menuItemJson.menuId.equals(fnMenuItem.menuId)) { + // FunctionalMenuItem should not be compared with itself + continue; + } + + if (!dublicatedName && fnMenuItem.text.equalsIgnoreCase(menuItemJson.text)) { + dublicatedName = true; + break; + } + } + if (dublicatedName) { + fieldsValidator.addProblematicFieldName("text"); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_CONFLICT); + fieldsValidator.errorCode = new Long(EPCommonSystemProperties.DUBLICATED_FIELD_VALUE_ECOMP_ERROR); + logger.debug(EELFLoggerDelegate.debugLogger, "In menuItemFieldsChecker, Error: we have an duplicate text field"); + } else if (StringUtils.isEmpty(menuItemJson.text) && menuItemJson.menuId == null) { + // text must be non empty for a create. For an edit, can be empty, which means it is a move request. + // a null menuId indicates a create. + fieldsValidator.addProblematicFieldName("text"); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_BAD_REQUEST); + logger.debug(EELFLoggerDelegate.debugLogger, "In menuItemFieldsChecker, Error: we have an empty text field"); + } else { + // The url, appid, and roles must either be all filled or all empty. + Boolean urlIsEmpty = StringUtils.isEmpty(menuItemJson.url); + Boolean rolesIsEmpty = menuItemJson.getRoles() == null || menuItemJson.getRoles().isEmpty(); + Boolean appidIsEmpty = menuItemJson.appid == null || menuItemJson.appid == 0; + logger.debug(EELFLoggerDelegate.debugLogger, "LR: menuItemfieldsChecker: urlIsEmpty: " + urlIsEmpty + "; rolesIsEmpty: " + rolesIsEmpty + "; appidIsEmpty: " + appidIsEmpty +"\n"); + if (!((urlIsEmpty && rolesIsEmpty && appidIsEmpty) || (!urlIsEmpty && !rolesIsEmpty && !appidIsEmpty))) + { + fieldsValidator.addProblematicFieldName("url,roles,appid"); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_BAD_REQUEST); + logger.debug(EELFLoggerDelegate.debugLogger, "In menuItemFieldsChecker, Error: we don't have: either all 3 fields empty or all 3 fields nonempty"); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "In menuItemFieldsChecker, Success: either all 3 fields empty or all 3 fields nonempty"); + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while validating the FunctionalMenuItems. Details: " + EcompPortalUtils.getStackTrace(e)); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + + return fieldsValidator; + } + + // Turn foreign key checks on or off + protected void setForeignKeys(Session localSession, Boolean on) { + String keyCheck = "0"; + if (on) { + keyCheck = "1"; + } + String sql = "set FOREIGN_KEY_CHECKS="+keyCheck; + logQuery(sql); + Query query = localSession.createSQLQuery(sql); + query.executeUpdate(); + } + + public FieldsValidator createFunctionalMenuItem(FunctionalMenuItemWithRoles menuItemJson) { + FieldsValidator fieldsValidator = menuItemFieldsChecker(menuItemJson); + if (fieldsValidator.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + logger.debug(EELFLoggerDelegate.debugLogger, "LR: createFunctionalMenuItem: test 1"); + boolean result = false; + Session localSession = null; + Transaction transaction = null; + try { + FunctionalMenuItem menuItem = new FunctionalMenuItem(); + menuItem.appid = menuItemJson.appid; + menuItem.setRoles(menuItemJson.getRoles()); + menuItem.url = menuItemJson.url; + menuItem.text = menuItemJson.text; + menuItem.parentMenuId = menuItemJson.parentMenuId; + menuItem.active_yn = "Y"; + localSession = sessionFactory.openSession(); + + // If the app is disabled, deactivate the menu item. + if (menuItemJson.appid != null) { + Long appidLong = Long.valueOf(menuItemJson.appid); + EPApp app = (EPApp) localSession.get(EPApp.class, appidLong); + if (app != null && ! app.getEnabled()) { + menuItem.active_yn = "N"; + } + } + + // Set the column number to 1 higher than the highest column + // number under this parent. + Criteria criteria = localSession.createCriteria(FunctionalMenuItem.class); + criteria.setProjection(Projections.max("column")); + criteria.add(Restrictions.eq("parentMenuId", menuItem.parentMenuId)); + Integer maxColumn = (Integer) criteria.uniqueResult(); + if (maxColumn == null) { + maxColumn = 0; + } + menuItem.column = maxColumn + 1; + logger.debug(EELFLoggerDelegate.debugLogger, "about to create menu item: " + menuItem.toString()); + + transaction = localSession.beginTransaction(); + // localSession.saveOrUpdate(newMenuItem); + localSession.save(menuItem); + Long menuid = menuItem.menuId; + menuItemJson.menuId = menuid; + logger.debug(EELFLoggerDelegate.debugLogger, "after saving menu object, new id: " + menuid); + + // Next, save all the roles + + addRoles(menuItemJson, localSession); + transaction.commit(); + result = true; + } catch (Exception e) { + EcompPortalUtils.rollbackTransaction(transaction, + "createFunctionalMenuItem rollback, exception = " + e); + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "createFunctionalMenuItem"); + } + if (result) { + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "LR: createFunctionalMenuItem: no result. setting httpStatusCode to " + + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } else { + logger.error(EELFLoggerDelegate.errorLogger, "FunctionalMenuServiceImpl.createFunctionalMenuItem: bad request"); + } + return fieldsValidator; + } + + /* Add all the roles in the menu item to the database */ + public void addRoles(FunctionalMenuItemWithRoles menuItemJson, Session localSession) { + logger.debug(EELFLoggerDelegate.debugLogger, "entering addRoles."); + List roles = menuItemJson.getRoles(); + if (roles != null && roles.size() > 0) { + Integer appid = menuItemJson.appid; + Long menuid = menuItemJson.menuId; + for (Integer roleid : roles) { + logger.debug(EELFLoggerDelegate.debugLogger, "about to create record for role: " + roleid); + FunctionalMenuRole role = new FunctionalMenuRole(); + role.appId = appid; + role.menuId = menuid; + role.roleId = roleid; + localSession.save(role); + logger.debug(EELFLoggerDelegate.debugLogger, "after saving role menu object, new id: " + role.id); + } + } + } + + /* Delete all the roles associated with the menu item from the database */ + public void deleteRoles(Long menuId) { + dataAccessService.deleteDomainObjects(FunctionalMenuRole.class, "menu_id='" + menuId + "'", null); + } + + /* Delete all favorites associated with the menu item from the database */ + public void deleteFavorites(Long menuId) { + dataAccessService.deleteDomainObjects(FavoritesFunctionalMenuItem.class, "menu_id='" + menuId + "'", null); + } + + private Boolean parentMenuIdEqual(Integer menuId1, Integer menuId2) { + return ((menuId1 == null && menuId2 == null) || (menuId1 != null && menuId1.equals(menuId2))); + } + + private void updateColumnForSiblings(Session localSession, Long menuId, Integer oldParentMenuId, + Integer newParentMenuId, Integer oldColumn, Integer newColumn) { + logger.debug(EELFLoggerDelegate.debugLogger, "entering updateColumnForSiblings"); + Criteria criteria = localSession.createCriteria(FunctionalMenuItem.class); + criteria.add(Restrictions.ne("menuId", menuId)); + if (parentMenuIdEqual(oldParentMenuId, newParentMenuId)) { + logger.debug(EELFLoggerDelegate.debugLogger, "moving under the same parent"); + // We are moving to a new position under the same parent + if (newParentMenuId == null) { + logger.debug(EELFLoggerDelegate.debugLogger, "newParentMenuId is null, so using isNull"); + criteria.add(Restrictions.isNull("parentMenuId")); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "newParentMenuId is NOT null, so using eq"); + criteria.add(Restrictions.eq("parentMenuId", newParentMenuId)); + } + if (oldColumn > newColumn) { + logger.debug(EELFLoggerDelegate.debugLogger, "moving to a lower column under the same parent"); + // We are moving to a lower column under the same parent + criteria.add(Restrictions.ge("column", newColumn)); + criteria.add(Restrictions.lt("column", oldColumn)); + @SuppressWarnings("unchecked") + List menuItems = criteria.list(); + for (FunctionalMenuItem menuItem : menuItems) { + menuItem.column += 1; + localSession.save(menuItem); + } + } else if (oldColumn < newColumn) { + logger.debug(EELFLoggerDelegate.debugLogger, "moving to a higher column under the same parent"); + // We are moving to a higher column under the same parent + criteria.add(Restrictions.gt("column", oldColumn)); + criteria.add(Restrictions.le("column", newColumn)); + @SuppressWarnings("unchecked") + List menuItems = criteria.list(); + for (FunctionalMenuItem menuItem : menuItems) { + menuItem.column -= 1; + localSession.save(menuItem); + } + } else { + // No info has changed + logger.debug(EELFLoggerDelegate.debugLogger, "no info has changed, so we are not moving"); + } + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "moving under a new parent"); + // We are moving under a new parent. + + // Adjust the children under the old parent + logger.debug(EELFLoggerDelegate.debugLogger, "about to adjust the children under the old parent"); + + // If the parentId is null, must check for its children differently + if (oldParentMenuId == null) { + logger.debug(EELFLoggerDelegate.debugLogger, "oldParentMenuId is null, so using isNull"); + criteria.add(Restrictions.isNull("parentMenuId")); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "oldParentMenuId is NOT null, so using eq"); + criteria.add(Restrictions.eq("parentMenuId", oldParentMenuId)); + } + + criteria.add(Restrictions.gt("column", oldColumn)); + @SuppressWarnings("unchecked") + List menuItems1 = criteria.list(); + for (FunctionalMenuItem menuItem : menuItems1) { + menuItem.column -= 1; + localSession.save(menuItem); + } + // Adjust the children under the new parent. + logger.debug(EELFLoggerDelegate.debugLogger, "about to adjust the children under the new parent"); + logger.debug(EELFLoggerDelegate.debugLogger, "get all menu items where menuId!=" + menuId + "; parentMenuId==" + newParentMenuId + + "; column>=" + newColumn); + criteria = localSession.createCriteria(FunctionalMenuItem.class); + criteria.add(Restrictions.ne("menuId", menuId)); + if (newParentMenuId == null) { + logger.debug(EELFLoggerDelegate.debugLogger, "newParentMenuId is null, so using isNull"); + criteria.add(Restrictions.isNull("parentMenuId")); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "newParentMenuId is NOT null, so using eq"); + criteria.add(Restrictions.eq("parentMenuId", newParentMenuId)); + } + + criteria.add(Restrictions.ge("column", newColumn)); + @SuppressWarnings("unchecked") + List menuItems2 = criteria.list(); + if (menuItems2 != null) { + logger.debug(EELFLoggerDelegate.debugLogger, "found " + menuItems2.size() + " menu items"); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "found null menu items"); + } + for (FunctionalMenuItem menuItem : menuItems2) { + menuItem.column += 1; + localSession.save(menuItem); + } + } + logger.debug(EELFLoggerDelegate.debugLogger, "done with updateColumnForSiblings"); + } + + public void removeAppInfo(Session localSession, Long menuId) { + // Remove the url, role, and app info from a menu item + FunctionalMenuItem menuItem = (FunctionalMenuItem) localSession.get(FunctionalMenuItem.class, menuId); + menuItem.url = ""; + deleteRoles(menuId); + } + + public FieldsValidator editFunctionalMenuItem(FunctionalMenuItemWithRoles menuItemJson) { + boolean result = false; + Session localSession = null; + Transaction transaction = null; + Long menuId = menuItemJson.menuId; + + logger.debug(EELFLoggerDelegate.debugLogger, "LR: editFunctionalMenuItem: test 1"); + FieldsValidator fieldsValidator = menuItemFieldsChecker(menuItemJson); + if (fieldsValidator.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + // TODO: make sure menuId is here. And, it might not already exist + // in db table. + if (menuId == null) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_BAD_REQUEST); + logger.error(EELFLoggerDelegate.errorLogger, "FunctionalMenuServiceImpl.editFunctionalMenuItem: bad request"); + } else { + // To simplify the code, assume we will have a transaction + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + + // Get the existing info associated with menuItem from the DB + FunctionalMenuItem menuItem = (FunctionalMenuItem) localSession.get(FunctionalMenuItem.class, menuId); + Integer oldColumn = menuItem.column; + Integer oldParentMenuId = menuItem.parentMenuId; + Integer newColumn = menuItemJson.column; + Integer newParentMenuId = menuItemJson.parentMenuId; + + logger.debug(EELFLoggerDelegate.debugLogger, "prev info: column: " + oldColumn + "; parentMenuId: " + oldParentMenuId); + + if (menuItemJson.appid != null && menuItemJson.getRoles() != null && !menuItemJson.getRoles().isEmpty() + && menuItemJson.url != null && !menuItemJson.url.isEmpty() && menuItemJson.text != null + && !menuItemJson.text.isEmpty()) { + // Scenario: appid, roles, url and text are all non-null. + // This menu item is associated with an app. + // (Note: this should only occur for a bottom-level menu + // item with no children.) + // 1) Remove all the records from fn_menu_functional_role + // for this menuId. + // 2) Add records to the fn_menu_function_role table for the + // appId and each roleId + // 3) Update the url and text for this menu item. + + // Because of foreign key constraints, delete the roles, + // then update the menuItem then add the roles. + deleteRoles(menuId); + // Assumption: this is not a Move, so don't change the + // parentMenuId and column. + menuItem.appid = menuItemJson.appid; + menuItem.setRoles(menuItemJson.getRoles()); + menuItem.url = menuItemJson.url; + menuItem.text = menuItemJson.text; + + // If the app is disabled, deactivate the menu item. + Long appidLong = Long.valueOf(menuItemJson.appid); + EPApp app = (EPApp) localSession.get(EPApp.class, appidLong); + if (app != null && ! app.getEnabled()) { + menuItem.active_yn = "N"; + } else { + menuItem.active_yn = "Y"; + } + + + localSession.update(menuItem); + addRoles(menuItemJson, localSession); + + } else if (menuItemJson.appid == null && (menuItemJson.getRoles() == null || menuItemJson.getRoles().isEmpty()) + && (menuItemJson.url == null || menuItemJson.url.isEmpty()) && menuItemJson.text != null + && !menuItemJson.text.isEmpty()) { + // Scenario: appid, roles and url are all null; text is + // non-null. + // This menu item is NOT associated with an app. + // 1) Remove all the records from fn_menu_functional_role + // for this menuId + // (in case it was previously associated with an app). + // 2) Update the text for this menu item. + // 3) Set the url to "" + deleteRoles(menuId); + // Assumption: this is not a Move, so don't change the + // parentMenuId and column. + menuItem.text = menuItemJson.text; + menuItem.url = ""; + menuItem.active_yn = "Y"; + localSession.update(menuItem); + + } else if (newColumn != null) { + // This is a "move" request. + // Menu item has been moved to a different position under + // the same parent, or under a new parent. + logger.debug(EELFLoggerDelegate.debugLogger, "Doing a move operation."); + if (parentMenuIdEqual(oldParentMenuId, newParentMenuId)) { + // The parent is the same. We have just changed the + // column + logger.debug(EELFLoggerDelegate.debugLogger, "moving under the same parent"); + menuItem.column = newColumn; + localSession.update(menuItem); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "moving under a different parent"); + menuItem.parentMenuId = newParentMenuId; + menuItem.column = newColumn; + localSession.update(menuItem); + // If we are moving under a new parent, must delete any + // app/role info from + // the new parent, since it is no longer a leaf menu + // item and cannot have app info + // associated with it. The front end will have warned + // the user and gotten confirmation. + if (menuItemJson.parentMenuId != null) { + Long parentMenuIdLong = new Long(menuItemJson.parentMenuId); + removeAppInfo(localSession, parentMenuIdLong); + // deleteRoles(parentMenuIdLong); + } + } + // must update the column for all old and new sibling menu + // items + updateColumnForSiblings(localSession, menuId, oldParentMenuId, newParentMenuId, oldColumn, + newColumn); + } + + transaction.commit(); + logger.debug(EELFLoggerDelegate.debugLogger, "LR: editFunctionalMenuItem: finished committing transaction"); + result = true; + } catch (Exception e) { + EcompPortalUtils.rollbackTransaction(transaction, + "createFunctionalMenuItem rollback, exception = " + e); + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "editFunctionalMenuItem"); + } + + if (result) { + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "LR: createFunctionalMenuItem: no result. setting httpStatusCode to " + + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + } + + return fieldsValidator; + } + + public FieldsValidator deleteFunctionalMenuItem(Long menuId) { + FieldsValidator fieldsValidator = new FieldsValidator(); + logger.debug(EELFLoggerDelegate.debugLogger, "LR: deleteFunctionalMenuItem: test 1"); + boolean result = false; + Session localSession = null; + Transaction transaction = null; + + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + // We must turn off foreign keys before deleting a menu item. Otherwise there will be a + // constraint violation from the ancestors table. + setForeignKeys(localSession, false); + deleteRoles(menuId); + logger.debug(EELFLoggerDelegate.debugLogger, "deleteFunctionalMenuItem: after deleting roles"); + deleteFavorites(menuId); + logger.debug(EELFLoggerDelegate.debugLogger, "deleteFunctionalMenuItem: after deleting favorites"); + localSession.delete(localSession.get(FunctionalMenuItem.class, menuId)); + logger.debug(EELFLoggerDelegate.debugLogger, "deleteFunctionalMenuItem: about to commit"); + transaction.commit(); + result = true; + } catch (Exception e) { + EcompPortalUtils.rollbackTransaction(transaction, + "deleteFunctionalMenuItem rollback, exception = " + e); + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "deleteFunctionalMenuItem"); + } + if (result) { + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "LR: deleteFunctionalMenuItem: no result. setting httpStatusCode to " + + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + return fieldsValidator; + } + + // Regenerate the fn_menu_functional_ancestors table, which is used + // by the queries that return the functional menu items. + public FieldsValidator regenerateAncestorTable() { + FieldsValidator fieldsValidator = new FieldsValidator(); + Session localSession = null; + Transaction transaction = null; + + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + String sql = "DELETE FROM fn_menu_functional_ancestors"; + logQuery(sql); + Query query = localSession.createSQLQuery(sql); + query.executeUpdate(); + logger.debug(EELFLoggerDelegate.debugLogger, "regenerateAncestorTable: finished query 1"); + + sql = "ALTER TABLE fn_menu_functional_ancestors AUTO_INCREMENT=1"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + logger.debug(EELFLoggerDelegate.debugLogger, "regenerateAncestorTable: reset AUTO_INCREMENT to 1"); + + int depth = 0; + sql = "INSERT INTO fn_menu_functional_ancestors(menu_id, ancestor_menu_id, depth) " + + "SELECT m.menu_id, m.menu_id, " + depth + " FROM fn_menu_functional m"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + logger.debug(EELFLoggerDelegate.debugLogger, "regenerateAncestorTable: finished query 2"); + for (depth = 0; depth < 3; depth++) { + int depthPlusOne = depth + 1; + sql = "INSERT INTO fn_menu_functional_ancestors(menu_id, ancestor_menu_id, depth) " + + " SELECT a.menu_id, m.parent_menu_id, " + depthPlusOne + + " FROM fn_menu_functional m, fn_menu_functional_ancestors a " + " WHERE a.depth='" + depth + + "' AND " + " a.ancestor_menu_id = m.menu_id AND " + " m.parent_menu_id != '-1'"; + logQuery(sql); + query = localSession.createSQLQuery(sql); + query.executeUpdate(); + } + logger.debug(EELFLoggerDelegate.debugLogger, "regenerateAncestorTable: finished query 3"); + transaction.commit(); + } catch (Exception e) { + EcompPortalUtils.rollbackTransaction(transaction, + "regenerateAncestorTable rollback, exception = " + e); + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "regenerateAncestorTable"); + } + return fieldsValidator; + } + + private void logQuery(String sql) { + logger.debug(EELFLoggerDelegate.debugLogger, "Executing query: " + sql); + } + + public FieldsValidator setFavoriteItem(FavoritesFunctionalMenuItem menuItemJson) { + boolean result = false; + FieldsValidator fieldsValidator = new FieldsValidator(); + + Session localSession = null; + Transaction transaction = null; + + try { + logger.debug(EELFLoggerDelegate.debugLogger, String.format("Before adding favorite for user id:{0} and menu id:{1} ",menuItemJson.userId,menuItemJson.menuId)); + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + localSession.save(menuItemJson); + transaction.commit(); + result = true; + logger.debug(EELFLoggerDelegate.debugLogger, String.format("After adding favorite for user id:{0} and menu id:{1} ",menuItemJson.userId,menuItemJson.menuId)); + } catch (Exception e) { + EcompPortalUtils.rollbackTransaction(transaction,"setFavoriteItem rollback, exception = " + e); + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "setFavoriteItem"); + } + + if(result) { + } + else { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + + return fieldsValidator; + } + + public List getFavoriteItems(Long userId) { + try { + logger.debug(EELFLoggerDelegate.debugLogger, "Before getting favorites for user id: " + userId); + + // Divide this into 2 queries: one which returns the favorites items associated with Restricted apps, + // and one that returns all the other favorites items. Then we can easily add the boolean flag + // restrictedApp to each FavoritesFunctionalMenuItemJson, to be used by the front end. + + String sql = "SELECT f.user_id,f.menu_id,m.text,m.url " + + " FROM fn_menu_favorites f, fn_menu_functional m, fn_menu_functional_roles mr " + + " WHERE f.user_id='" + userId + "' AND f.menu_id = m.menu_id " + + " AND f.menu_id = mr.menu_id " + + " AND mr.role_id = '" + RESTRICTED_APP_ROLE_ID + "' "; + + @SuppressWarnings("unchecked") + List menuItems = dataAccessService.executeSQLQuery(sql, FavoritesFunctionalMenuItemJson.class, null); + for (FavoritesFunctionalMenuItemJson menuItem : menuItems) { + menuItem.restrictedApp = true; + } + + sql = "SELECT DISTINCT f.user_id,f.menu_id,m.text,m.url " + + " FROM fn_menu_favorites f, fn_menu_functional m, fn_menu_functional_roles mr " + + " WHERE f.user_id='" + userId + "' AND f.menu_id = m.menu_id " + + " AND f.menu_id = mr.menu_id " + + " AND mr.role_id != '" + RESTRICTED_APP_ROLE_ID + "' "; + @SuppressWarnings("unchecked") + List menuItems2 = dataAccessService.executeSQLQuery(sql, FavoritesFunctionalMenuItemJson.class, null); + for (FavoritesFunctionalMenuItemJson menuItem : menuItems2) { + menuItem.restrictedApp = false; + menuItems.add(menuItem); + } + + return menuItems; + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred in FunctionalMenuServiceImpl.getFavoriteItems. Details: " + EcompPortalUtils.getStackTrace(e)); + List menuItems = new ArrayList(); + return menuItems; + } + } + + public FieldsValidator removeFavoriteItem(Long userId, Long menuId) { + boolean result = false; + FieldsValidator fieldsValidator = new FieldsValidator(); + + Session localSession = null; + Transaction transaction = null; + + try { + + FavoritesFunctionalMenuItem menuItemJson = new FavoritesFunctionalMenuItem(); + menuItemJson.userId = userId; + menuItemJson.menuId = menuId; + + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + localSession.delete(menuItemJson); + localSession.flush(); + transaction.commit(); + result = true; + logger.debug(EELFLoggerDelegate.debugLogger, String.format("After removing favorite for user id: " + userId + "; menu id: " + menuId)); + } catch (Exception e) { + EcompPortalUtils.rollbackTransaction(transaction,"removeFavoriteItem rollback, exception = " + e); + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "removeFavoriteItem"); + } + + if(result) { + } + else { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + + return fieldsValidator; + } + + @Override + public void assignHelpURLs(List menuItems) { + try { + String user_guide_link = SystemProperties.getProperty(EPCommonSystemProperties.USER_GUIDE_URL); + + for(FunctionalMenuItem menuItem: menuItems){ + if(menuItem.text.equalsIgnoreCase("Contact Us")){ + menuItem.setUrl("contactUs"); + //menuItem.setRestrictedApp(true); + } + if(menuItem.text.equalsIgnoreCase("Get Access")) { + menuItem.setUrl("getAccess"); + } + if(menuItem.text.equalsIgnoreCase("User Guide")) { + menuItem.setUrl(user_guide_link); + menuItem.setRestrictedApp(true); + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "assignHelpURLs process failed. Details: " + EcompPortalUtils.getStackTrace(e)); + } + + } + + public List getFunctionalMenuRole() { + String sql = "SELECT * from fn_menu_functional_roles"; + logQuery(sql); + logger.debug(EELFLoggerDelegate.debugLogger, "getFunctionalMenuRole: logged the query"); + + @SuppressWarnings("unchecked") + List functionalMenuRole = dataAccessService.executeSQLQuery(sql, FunctionalMenuRole.class, null); + + return functionalMenuRole; + } + + @SuppressWarnings("unchecked") + @Override + public List getUserAppRolesList(String userId) { + Map params = new HashMap<>(); + params.put("userId", userId); + + List userAppRoles = null; + try { + userAppRoles = dataAccessService + .executeNamedQuery("getUserApproles", params, null); + } catch (Exception e) { + // TODO Auto-generated catch block + logger.error(EELFLoggerDelegate.errorLogger, "getUserAppRolesList failed", e); + } + return userAppRoles; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/GetAccessService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/GetAccessService.java new file mode 100644 index 00000000..8b5e72e5 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/GetAccessService.java @@ -0,0 +1,33 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.GetAccessResult; + +public interface GetAccessService { + /** + * @return One entry per application function AND role; i.e., each application + * appears many times. + */ + List getAppAccessList(EPUser user); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/GetAccessServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/GetAccessServiceImpl.java new file mode 100644 index 00000000..37af1dab --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/GetAccessServiceImpl.java @@ -0,0 +1,61 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.GetAccessResult; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; + +@Service("getAccessService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class GetAccessServiceImpl implements GetAccessService{ + + @Autowired + private DataAccessService dataAccessService; + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.GetAccessService#getAppAccessList() + */ + @SuppressWarnings("unchecked") + @Override + public List getAppAccessList(EPUser user) { + final Map params = new HashMap<>(); + List appAccessList = null; + params.put("userId", user.getId()); + appAccessList = dataAccessService + .executeNamedQuery("getAppAccessFunctionRole", params, null); + return appAccessList; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ManifestService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ManifestService.java new file mode 100644 index 00000000..7a10568d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ManifestService.java @@ -0,0 +1,33 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.io.IOException; +import java.util.jar.Attributes; + +public interface ManifestService { + /** + * Gets the content of the webapp manifest file META-INF/MANIFEST.MF. + * + * @return Attributes object with key-value pairs from the manifest + * @throws IOException + */ + public Attributes getWebappManifest() throws IOException; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ManifestServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ManifestServiceImpl.java new file mode 100644 index 00000000..3d0f34a2 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/ManifestServiceImpl.java @@ -0,0 +1,62 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.io.IOException; +import java.io.InputStream; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import javax.servlet.ServletContext; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; + +@Service("manifestService") +@EnableAspectJAutoProxy +@EPMetricsLog +public class ManifestServiceImpl implements ManifestService { + @Autowired + ServletContext context; + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.ManifestService#getWebappManifest() + */ + public Attributes getWebappManifest() throws IOException { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ManifestServiceImpl.class); + // Path to resource on classpath + final String MANIFEST_RESOURCE_PATH = "/META-INF/MANIFEST.MF"; + // Manifest is formatted as Java-style properties + try { + InputStream inputStream = context.getResourceAsStream(MANIFEST_RESOURCE_PATH); + Manifest manifest = new Manifest(inputStream); + inputStream.close(); + return manifest.getMainAttributes(); + } catch (IOException e) { + logger.error(EELFLoggerDelegate.errorLogger, "getWebappManifest: failed to read/find manifest"); + throw e; + } + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceProxyService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceProxyService.java new file mode 100644 index 00000000..00fc9ad8 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceProxyService.java @@ -0,0 +1,59 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import javax.servlet.http.HttpServletRequest; + +import org.openecomp.portalapp.portal.domain.EPUser; +import org.springframework.web.client.RestClientException; + +public interface MicroserviceProxyService { + + /** + * Gets the specific microservice from table ep_microservice, communicates + * to microservice with the specified security type. The microservice sends + * back the response. + * + * Gets the data while testing the microservice with no widget associated with + * + * @param serviceId + * Id of microservice to be used + * @return response sent from microservice + * @throws Exception + */ + String proxyToDestination(long serviceId, EPUser user, HttpServletRequest request) throws Exception; + + + + /** + * Gets the microservice data based on the user id and widget id. Different + * users have his/her own parameters for one widget. The method sends back + * the response. + * + * @param widgetId + * Id of widget to be used + * @param user + * User information + * @param request + * @return response sent from microserivce + * @throws Exception + */ + String proxyToDestinationByWidgetId(long widgetId, EPUser user, HttpServletRequest request) throws Exception; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceProxyServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceProxyServiceImpl.java new file mode 100644 index 00000000..3a520c88 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceProxyServiceImpl.java @@ -0,0 +1,222 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.codec.binary.Base64; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.MicroserviceData; +import org.openecomp.portalapp.portal.domain.MicroserviceParameter; +import org.openecomp.portalapp.portal.domain.WidgetCatalog; +import org.openecomp.portalapp.portal.domain.WidgetCatalogParameter; +import org.openecomp.portalapp.portal.domain.WidgetServiceHeaders; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.util.CipherUtil; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +@Service("microserviceProxyService") +@EnableAspectJAutoProxy +@EPMetricsLog +public class MicroserviceProxyServiceImpl implements MicroserviceProxyService { + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MicroserviceProxyServiceImpl.class); + private static final String BASIC_AUTH = "Basic Authentication"; + private static final String NO_AUTH = "No Authentication"; + private static final String COOKIE_AUTH = "Cookie based Authentication"; + private static final String QUESTION_MARK = "?"; + private static final String ADD_MARK = "&"; + + String whatService = "widgets-service"; + + @Autowired + private ConsulHealthService consulHealthService; + + @Autowired + MicroserviceService microserviceService; + + @Autowired + WidgetParameterService widgetParameterService; + + RestTemplate template = new RestTemplate(); + + @Override + public String proxyToDestination(long serviceId, EPUser user, HttpServletRequest request) throws Exception { + + String response = null; + + // get the microservice object by the id + MicroserviceData data = microserviceService.getMicroserviceDataById(serviceId); + + // No such microservice available + if (data == null) { + return response; + } + List params = data.getParameterList(); + if (data.getSecurityType().equals(NO_AUTH)) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(headers); + + String url = microserviceUrlConverter(data, params); + response = template.exchange(url, HttpMethod.GET, entity, String.class).getBody(); + + } else if (data.getSecurityType().equals(BASIC_AUTH)) { + // encoding the username and password + String plainCreds = data.getUsername() + ":" + decryptedPassword(data.getPassword()); + byte[] plainCredsBytes = plainCreds.getBytes(); + byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); + String base64Creds = new String(base64CredsBytes); + + HttpHeaders headers = new HttpHeaders(); + headers.add("Authorization", "Basic " + base64Creds); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(headers); + + String url = microserviceUrlConverter(data, params); + response = template.exchange(url, HttpMethod.GET, entity, String.class).getBody(); + } else if (data.getSecurityType().equals(COOKIE_AUTH)) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + String rawCookie = request.getHeader("Cookie"); + headers.add("Cookie", rawCookie); + HttpEntity entity = new HttpEntity(headers); + + String url = microserviceUrlConverter(data, params); + response = template.exchange(url, HttpMethod.GET, entity, String.class).getBody(); + } + return response; + } + + @Override + public String proxyToDestinationByWidgetId(long widgetId, EPUser user, HttpServletRequest request) throws Exception { + + String response = null; + + ResponseEntity ans = (ResponseEntity) template.exchange( + "https://" + consulHealthService.getServiceLocation(whatService) + + "/widget/microservices/widgetCatalog/parameters/" + widgetId, + HttpMethod.GET, new HttpEntity(WidgetServiceHeaders.getInstance()), Long.class); + Long serviceId = ans.getBody(); + + // get the microservice object by the id + MicroserviceData data = microserviceService.getMicroserviceDataById(serviceId); + + // No such microservice available + if (data == null) { + return response; + } + + List params = data.getParameterList(); + + for (MicroserviceParameter p : params) { + WidgetCatalogParameter userValue = widgetParameterService.getUserParamById(widgetId, user.getId(), p.getId()); + if (userValue != null) + p.setPara_value(userValue.getUser_value()); + } + + if (data.getSecurityType().equals(NO_AUTH)) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(headers); + + String url = microserviceUrlConverter(data, params); + try { + response = template.exchange(url, HttpMethod.GET, entity, String.class).getBody(); + } catch (HttpClientErrorException e) { + throw e; + } + } else if (data.getSecurityType().equals(BASIC_AUTH)) { + // encoding the username and password + String plainCreds = data.getUsername() + ":" + decryptedPassword(data.getPassword()); + byte[] plainCredsBytes = plainCreds.getBytes(); + byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); + String base64Creds = new String(base64CredsBytes); + + HttpHeaders headers = new HttpHeaders(); + headers.add("Authorization", "Basic " + base64Creds); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(headers); + + String url = microserviceUrlConverter(data, params); + try { + response = template.exchange(url, HttpMethod.GET, entity, String.class).getBody(); + } catch (HttpClientErrorException e) { + throw e; + } + } else if (data.getSecurityType().equals(COOKIE_AUTH)) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + String rawCookie = request.getHeader("Cookie"); + headers.add("Cookie", rawCookie); + HttpEntity entity = new HttpEntity(headers); + + String url = microserviceUrlConverter(data, params); + try { + response = template.exchange(url, HttpMethod.GET, entity, String.class).getBody(); + } catch (HttpClientErrorException e) { + throw e; + } + } + return response; + } + + private String decryptedPassword(String encryptedPwd) throws Exception { + String result = ""; + if (encryptedPwd != null & encryptedPwd.length() > 0) { + try { + result = CipherUtil.decrypt(encryptedPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "decryptedPassword failed", e); + throw e; + } + } + return result; + } + + private String microserviceUrlConverter(MicroserviceData data, List params) { + String url = data.getUrl(); + for (int i = 0; i < params.size(); i++) { + if (i == 0) { + url += QUESTION_MARK; + } + url += params.get(i).getPara_key() + "=" + params.get(i).getPara_value(); + if (i != (params.size() - 1)) { + url += ADD_MARK; + } + } + return url; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceService.java new file mode 100644 index 00000000..cfc7ddb1 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceService.java @@ -0,0 +1,86 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.MicroserviceData; +import org.openecomp.portalapp.portal.domain.MicroserviceParameter; + +public interface MicroserviceService { + + /** + * Get all microservices from the ep_microservice + * + * @return list of all microservices + * @throws Exception + */ + List getMicroserviceData() throws Exception; + + /** + * Gets the specified microservice with id from ep_microservice + * + * @param id + * ID of microservice to be fetched + * @return the microservice with the specified id + */ + MicroserviceData getMicroserviceDataById(long id); + + /** + * Saves the specified microservice to the table ep_microservice + * + * @param newService + * Content of microservice to be saved + * @return new microservice id + * @throws Exception + */ + Long saveMicroservice(MicroserviceData newService) throws Exception; + + void saveServiceParameters(long serviceId, List list) throws Exception; + + /** + * Deletes the specified microservice from all tables where the serviceId is + * used + * + * @param serviceId + * @throws Exception + */ + void deleteMicroservice(long serviceId) throws Exception; + + /** + * Updates the specified microservice from all tables where the serviceId is + * used + * + * @param serviceId + * Id of microservice to be updated + * @param newService + * Content of microservice to be updated + * @throws Exception + */ + void updateMicroservice(long serviceId, MicroserviceData newService) throws Exception; + + /** + * Gets the Service parameters by the service Id + * @param serviceId + * @return + */ + List getParametersById(long serviceId); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceServiceImpl.java new file mode 100644 index 00000000..c0a2fa41 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/MicroserviceServiceImpl.java @@ -0,0 +1,212 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.criterion.Criterion; +import org.hibernate.criterion.Restrictions; +import org.openecomp.portalapp.portal.domain.BasicAuthCredentials; +import org.openecomp.portalapp.portal.domain.EPUserAppRolesRequestDetail; +import org.openecomp.portalapp.portal.domain.MicroserviceData; +import org.openecomp.portalapp.portal.domain.MicroserviceParameter; +import org.openecomp.portalapp.portal.domain.WidgetCatalog; +import org.openecomp.portalapp.portal.domain.WidgetServiceHeaders; +import org.openecomp.portalapp.portal.ecomp.model.PortalRestResponse; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.transport.CommonWidgetMeta; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.util.CipherUtil; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service("microserviceService") +@EnableAspectJAutoProxy +@EPMetricsLog +public class MicroserviceServiceImpl implements MicroserviceService { + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MicroserviceServiceImpl.class); + + + @Autowired + private DataAccessService dataAccessService; + + @Autowired + private SessionFactory sessionFactory; + + public Long saveMicroservice(MicroserviceData newService) throws Exception { + if (newService.getPassword() != null) + newService.setPassword(encryptedPassword(newService.getPassword())); + getDataAccessService().saveDomainObject(newService, null); + return newService.getId(); + } + + public void saveServiceParameters(long serviceId, List list) throws Exception { + for (int i = 0; i < list.size(); i++) { + MicroserviceParameter para = list.get(i); + para.setServiceId(serviceId); + getDataAccessService().saveDomainObject(para, null); + } + } + + @Override + public MicroserviceData getMicroserviceDataById(long id) { + MicroserviceData data = null; + try { + data = (MicroserviceData) dataAccessService + .getList(MicroserviceData.class, " where id = '" + id + "'", null, null).get(0); + data.setParameterList(getServiceParameters(id)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getMicroserviceDataById failed", e); + throw e; + } + return data; + } + + @SuppressWarnings("unchecked") + @Override + public List getMicroserviceData() throws Exception { + List list = (List) dataAccessService.getList(MicroserviceData.class, null); + for (int i = 0; i < list.size(); i++) { + if (list.get(i).getPassword() != null) + list.get(i).setPassword(decryptedPassword(list.get(i).getPassword())); + list.get(i).setParameterList(getServiceParameters(list.get(i).getId())); + } + return list; + } + + @SuppressWarnings("unchecked") + private List getServiceParameters(long serviceId) { + List list = (List) dataAccessService + .getList(MicroserviceParameter.class, " where service_id = '" + serviceId + "'", null, null); + return list; + } + + @Override + public void deleteMicroservice(long serviceId) throws Exception { + + try { + Map params = new HashMap(); + params.put("serviceId", Long.toString(serviceId)); + + dataAccessService.executeNamedQuery("deleteMicroserviceParameter", params, null); + dataAccessService.executeNamedQuery("deleteMicroservice", params, null); + + } catch (Exception e) { + e.printStackTrace(); + logger.error(EELFLoggerDelegate.errorLogger, "deleteMicroservice failed", e); + throw e; + } + } + + @SuppressWarnings("unchecked") + @Override + public void updateMicroservice(long serviceId, MicroserviceData newService) throws Exception { + try { + newService.setId(serviceId); + if (newService.getPassword() != null) + newService.setPassword(encryptedPassword(newService.getPassword())); + getDataAccessService().saveDomainObject(newService, null); + List oldService = getServiceParameters(serviceId); + boolean foundParam; + for (int i = 0; i < oldService.size(); i++) { + foundParam = false; + for (int n = 0; n < newService.getParameterList().size(); n++) { + if (newService.getParameterList().get(n).getId() == oldService.get(i).getId()) { + foundParam = true; + break; + } + } + if (foundParam == false) { + MicroserviceParameter pd = oldService.get(i); + Session localSession = sessionFactory.openSession(); + localSession.delete(pd); + localSession.flush(); + localSession.clear(); + } + + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "updateMicroservice failed", e); + throw e; + } + saveServiceParameters(serviceId, newService.getParameterList()); + } + + @Override + @SuppressWarnings("unchecked") + public List getParametersById(long serviceId) { + List restrictionsList = new ArrayList(); + Criterion contextIdCrit = Restrictions.eq("serviceId", serviceId); + restrictionsList.add(contextIdCrit); + List list = (List) dataAccessService + .getList(MicroserviceParameter.class, null, restrictionsList, null); + logger.debug(EELFLoggerDelegate.debugLogger, + "getParametersById: microservice parameters list size: " + list.size()); + return list; + } + + private String decryptedPassword(String encryptedPwd) throws Exception { + String result = ""; + if (encryptedPwd != null & encryptedPwd.length() > 0) { + try { + result = CipherUtil.decrypt(encryptedPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "decryptedPassword failed", e); + throw e; + } + } + return result; + } + + private String encryptedPassword(String decryptedPwd) throws Exception { + String result = ""; + if (decryptedPwd != null & decryptedPwd.length() > 0) { + try { + result = CipherUtil.encrypt(decryptedPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "encryptedPassword failed", e); + throw e; + } + } + return result; + } + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserAppService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserAppService.java new file mode 100644 index 00000000..7874fa74 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserAppService.java @@ -0,0 +1,42 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPUser; + +public interface PersUserAppService { + + /** + * Sets the appropriate code in the user personalization table to indicate + * the application is (de)selected and/or pending. + * + * @param user + * EP User + * @param app + * EP Application + * @param select + * True or false + * @param select + * True or false + */ + void setPersUserAppValue(EPUser user, EPApp app, boolean select, boolean pending); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserAppServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserAppServiceImpl.java new file mode 100644 index 00000000..6ff10ab4 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserAppServiceImpl.java @@ -0,0 +1,141 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EPUserApp; +import org.openecomp.portalapp.portal.domain.PersUserAppSelection; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; + +@Service("persUserAppService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class PersUserAppServiceImpl implements PersUserAppService { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(PersUserAppServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + @Autowired + private AdminRolesService adminRolesService; + @Autowired + private UserRolesService userRolesService; + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalapp.portal.service.UserAppSelectService# + * setAppCatalogSelection(org.openecomp.portalapp.portal.domain.EPUser, + * org.openecomp.portalapp.portal.transport.AppCatalogSelection) + */ + @Override + public void setPersUserAppValue(EPUser user, EPApp app, boolean select, boolean pending) { + if (user == null || app == null) + throw new IllegalArgumentException("setPersUserAppValue: Null values"); + + // Find the record for this user-app combo, if any + String filter = " where user_id = " + Long.toString(user.getId()) + " and app_id = " + + Long.toString(app.getId()); + @SuppressWarnings("unchecked") + List persList = dataAccessService.getList(PersUserAppSelection.class, filter, null, null); + + // Key constraint limits to 1 row + PersUserAppSelection persRow = null; + if (persList.size() == 1) + persRow = persList.get(0); + else + persRow = new PersUserAppSelection(null, user.getId(), app.getId(), null); + + if (app.getOpen()) { + // Pending status is not meaningful for open apps. + if (pending) + logger.error(EELFLoggerDelegate.errorLogger, + "setPersUserAppValue: invalid request, ignoring set-pending for open app"); + + // Open apps have same behavior for regular and admin users + if (select) { + // Selection of an open app requires a record + persRow.setStatusCode("S"); // show + dataAccessService.saveDomainObject(persRow, null); + } else { + // De-selection of an open app requires no record + if (persRow.getId() != null) + dataAccessService.deleteDomainObject(persRow, null); + } + } else { + // Non-open app. + + // Pending overrides select. + if (pending) { + persRow.setStatusCode("P"); + dataAccessService.saveDomainObject(persRow, null); + } else { + // Behavior depends on Portal (super) admin status, bcos an + // admin can force an app onto the dashboard. + boolean isPortalAdmin = adminRolesService.isSuperAdmin(user); + boolean adminUserHasAppRole = false; + if (isPortalAdmin) { + List roles = userRolesService.getCachedAppRolesForUser(app.getId(), user.getId()); + adminUserHasAppRole = (roles.size() > 0); + logger.debug(EELFLoggerDelegate.debugLogger, "setPersUserAppValue: app {}, admin user {}, role count {}", + app.getId(), user.getId(), roles.size()); + } + + if (select) { + if (isPortalAdmin && !adminUserHasAppRole) { + // The special case: portal admin, no role + persRow.setStatusCode("S"); // show + dataAccessService.saveDomainObject(persRow, null); + } else { + // User has role-based access to the app. + // Showing an accessible app requires no record. + if (persRow.getId() != null) + dataAccessService.deleteDomainObject(persRow, null); + } + } // select + else { + if (isPortalAdmin && !adminUserHasAppRole) { + // The special case: portal admin, no role + if (persRow.getId() != null) + dataAccessService.deleteDomainObject(persRow, null); + } else { + // User has role-based access to the app. + // Hiding an accessible app requires a record + persRow.setStatusCode("H"); // hide + dataAccessService.saveDomainObject(persRow, null); + } + } // deselect + } + } + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserWidgetService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserWidgetService.java new file mode 100644 index 00000000..d3a46d98 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserWidgetService.java @@ -0,0 +1,41 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import org.openecomp.portalapp.portal.domain.EPUser; + +public interface PersUserWidgetService { + + /** + * Sets the appropriate code in the user personalization table to indicate + * the application is (de)selected and/or pending. + * + * @param user + * EP User + * @param app + * EP Application + * @param select + * True or false + * @param select + * True or false + */ + void setPersUserAppValue(EPUser user, Long widgetId, boolean select); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserWidgetServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserWidgetServiceImpl.java new file mode 100644 index 00000000..9a45dea6 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PersUserWidgetServiceImpl.java @@ -0,0 +1,81 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.PersUserWidgetSelection; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; + +@Service("persUserWidgetService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class PersUserWidgetServiceImpl implements PersUserWidgetService{ + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(PersUserAppServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + + @Override + public void setPersUserAppValue(EPUser user, Long widgetId, boolean select) { + if (user == null || widgetId == null) + throw new IllegalArgumentException("setPersUserAppValue: Null values"); + + String filter = " where user_id = " + Long.toString(user.getId()) + " and widget_id = " + + Long.toString(widgetId); + @SuppressWarnings("unchecked") + List persList = dataAccessService.getList(PersUserWidgetSelection.class, filter, null, null); + + // Key constraint limits to 1 row + PersUserWidgetSelection persRow = null; + if (persList.size() == 1){ + persRow = persList.get(0); + } + else { + persRow = new PersUserWidgetSelection(null, user.getId(), widgetId, null); + } + if(select){ + if (persRow.getId() != null){ + dataAccessService.deleteDomainObject(persRow, null); + } + persRow.setStatusCode("S"); // show + dataAccessService.saveDomainObject(persRow, null); + } else{ + if (persRow.getId() != null){ + dataAccessService.deleteDomainObject(persRow, null); + } + persRow.setStatusCode("H"); // Hide + dataAccessService.saveDomainObject(persRow, null); + } + + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PortalAdminService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PortalAdminService.java new file mode 100644 index 00000000..55b3afd6 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PortalAdminService.java @@ -0,0 +1,31 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.PortalAdmin; + +public interface PortalAdminService { + List getPortalAdmins (); + FieldsValidator createPortalAdmin(String orgUserId); + FieldsValidator deletePortalAdmin(Long userId); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PortalAdminServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PortalAdminServiceImpl.java new file mode 100644 index 00000000..639bfc4c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/PortalAdminServiceImpl.java @@ -0,0 +1,210 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletResponse; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.PortalAdmin; +import org.openecomp.portalapp.portal.transport.PortalAdminUserRole; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; + +@Service("portalAdminService") +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class PortalAdminServiceImpl implements PortalAdminService { + + private String SYS_ADMIN_ROLE_ID = "1"; + private String ECOMP_APP_ID = "1"; + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(PortalAdminServiceImpl.class); + + @Autowired + private SessionFactory sessionFactory; + @Autowired + private DataAccessService dataAccessService; + @Autowired + SearchService searchService; + + @PostConstruct + private void init() { + SYS_ADMIN_ROLE_ID = SystemProperties.getProperty(SystemProperties.SYS_ADMIN_ROLE_ID); + ECOMP_APP_ID = SystemProperties.getProperty(EPCommonSystemProperties.ECOMP_APP_ID); + } + + public List getPortalAdmins() { + try { + String sql = "SELECT u.user_id, u.first_name, u.last_name, u.login_id " + + " FROM fn_user u, fn_user_role ur WHERE u.user_id = ur.user_id AND ur.role_id=" + + SYS_ADMIN_ROLE_ID; + logQuery(sql); + + @SuppressWarnings("unchecked") + List portalAdmins = dataAccessService.executeSQLQuery(sql, PortalAdmin.class, null); + logger.debug(EELFLoggerDelegate.debugLogger, "getPortalAdmins was successful"); + return portalAdmins; + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred while performing getPortalAdmins operation, Details: " + + EcompPortalUtils.getStackTrace(e)); + return null; + } + } + + @SuppressWarnings("unchecked") + public FieldsValidator createPortalAdmin(String orgUserId) { + FieldsValidator fieldsValidator = new FieldsValidator(); + logger.debug(EELFLoggerDelegate.debugLogger, "LR: createPortalAdmin: test 1"); + boolean result = false; + EPUser user = null; + boolean createNewUser = false; + List localUserList = dataAccessService.getList(EPUser.class, " where orgUserId='" + orgUserId + "'", + null, null); + if (localUserList.size() > 0) { + user = localUserList.get(0); + } else { + createNewUser = true; + } + + if (user!=null && isLoggedInUserPortalAdmin(user.getId())) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_CONFLICT); + logger.error(EELFLoggerDelegate.errorLogger, "User '" + user.getOrgUserId() + "' already has PortalAdmin role assigned."); + } + else if (user != null || createNewUser) { + Session localSession = null; + Transaction transaction = null; + try { + localSession = sessionFactory.openSession(); + + transaction = localSession.beginTransaction(); + if (createNewUser) { + user = this.searchService.searchUserByUserId(orgUserId); + if (user != null) { + // insert the user with active true in order to + // pass login phase. + user.setActive(true); + localSession.save(EPUser.class.getName(), user); + } + } + if (user != null) { + Long userid = user.getId(); + PortalAdminUserRole userRole = new PortalAdminUserRole(); + userRole.userId = userid; + userRole.roleId = Long.valueOf(SYS_ADMIN_ROLE_ID); + userRole.appId = Long.valueOf(ECOMP_APP_ID); + + localSession.save(PortalAdminUserRole.class.getName(), userRole); + } + // logger.debug(EELFLoggerDelegate.debugLogger, "after saving menu object, new id: " + userid); + + transaction.commit(); + result = true; + } catch (Exception e) { + EcompPortalUtils.rollbackTransaction(transaction, + "createPortalAdmin rollback, exception = " + e); + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "createPortalAdmin"); + } + if (!result) { + logger.debug(EELFLoggerDelegate.debugLogger, "LR: createPortalAdmin: no result. setting httpStatusCode to " + + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + logger.error(EELFLoggerDelegate.errorLogger, "PortalAdminServiceImpl.createPortalAdmin: bad request"); + } + } + return fieldsValidator; + } + + public FieldsValidator deletePortalAdmin(Long userId) { + FieldsValidator fieldsValidator = new FieldsValidator(); + logger.debug(EELFLoggerDelegate.debugLogger, "deletePortalAdmin: test 1"); + boolean result = false; + Session localSession = null; + Transaction transaction = null; + + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + dataAccessService.deleteDomainObjects(PortalAdminUserRole.class, + "user_id='" + userId + "' AND role_id='" + SYS_ADMIN_ROLE_ID + "'", null); + transaction.commit(); + result = true; + } catch (Exception e) { + EcompPortalUtils.rollbackTransaction(transaction, "deletePortalAdmin rollback, exception = " + e); + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "deletePortalAdmin"); + } + if (result) { + } else { + logger.debug(EELFLoggerDelegate.debugLogger, "deletePortalAdmin: no result. setting httpStatusCode to " + + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + return fieldsValidator; + } + + private void logQuery(String sql) { + logger.debug(EELFLoggerDelegate.debugLogger, "Executing query: " + sql); + } + + private boolean isLoggedInUserPortalAdmin(Long userId) { + try { + String sql = "SELECT u.user_id, u.first_name, u.last_name, u.login_id " + + " FROM fn_user u, fn_user_role ur " + + " WHERE u.user_id = ur.user_id " + + " AND ur.user_id=" + userId + + " AND ur.role_id=" + SYS_ADMIN_ROLE_ID; + + logQuery(sql); + + @SuppressWarnings("unchecked") + List portalAdmins = dataAccessService.executeSQLQuery(sql, PortalAdmin.class, null); + logger.debug(EELFLoggerDelegate.debugLogger, portalAdmins.toString()); + if (portalAdmins==null || portalAdmins.size()<=0) { + return false; + } + return true; + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while performing isLoggedInUserPortalAdmin operation, Details: " + EcompPortalUtils.getStackTrace(e)); + return false; + } + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SearchService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SearchService.java new file mode 100644 index 00000000..9aa24692 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SearchService.java @@ -0,0 +1,30 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import org.openecomp.portalapp.portal.domain.EPUser; + +public interface SearchService { + + public String searchUsersInPhoneBook(String searchString); + + public EPUser searchUserByUserId(String orgUserId); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SearchServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SearchServiceImpl.java new file mode 100644 index 00000000..88dc299f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SearchServiceImpl.java @@ -0,0 +1,175 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.transport.UserWithNameSurnameTitle; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Service("searchService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class SearchServiceImpl implements SearchService { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SearchServiceImpl.class); + + // TODO: the values below should be defined in other place + private static final int maxSizeOfSearchResult = 100; + + + @Autowired + EPLdapService ldapService; + + @Override + public String searchUsersInPhoneBook(String searchString) { + String orgUserId = null; + List tokens = EcompPortalUtils.parsingByRegularExpression(searchString, " "); + for (int i = 0; i < tokens.size(); i++) { // find orgUserId if possible and remove it from tokens + if (tokens.get(i).matches(".*\\d+.*")) { + orgUserId = tokens.get(i); + tokens.remove(i); + } + } + while (tokens.size() > 2) { // we use no more then first 2 tokens (orgUserId is removed, see above) + tokens.remove(tokens.size() - 1); + } + EPUser attrUser = new EPUser(); + attrUser.setOrgUserId(orgUserId); + List resultOfSearch = new ArrayList(), resultOfAdditionalSearch = null; + if (tokens.size() == 2) { + attrUser.setFirstName(tokens.get(0)); + attrUser.setLastName(tokens.get(1)); + resultOfSearch = this.searchUsersByAttrs(attrUser); + resultOfSearch = this.removeWrongFirstNames(resultOfSearch, tokens.get(0)); + resultOfSearch = this.removeWrongLastNames(resultOfSearch, tokens.get(1)); + if (resultOfSearch.size() < maxSizeOfSearchResult) { + attrUser.setFirstName(tokens.get(1)); + attrUser.setLastName(tokens.get(0)); + resultOfAdditionalSearch = this.searchUsersByAttrs(attrUser); + resultOfAdditionalSearch = this.removeWrongFirstNames(resultOfAdditionalSearch, tokens.get(1)); + resultOfAdditionalSearch = this.removeWrongLastNames(resultOfAdditionalSearch, tokens.get(0)); + } + } else if (tokens.size() == 1) { + attrUser.setFirstName(tokens.get(0)); + resultOfSearch = this.searchUsersByAttrs(attrUser); + resultOfSearch = this.removeWrongFirstNames(resultOfSearch, tokens.get(0)); + if (resultOfSearch.size() < maxSizeOfSearchResult) { + attrUser.setFirstName(null); + attrUser.setLastName(tokens.get(0)); + resultOfAdditionalSearch = this.searchUsersByAttrs(attrUser); + resultOfAdditionalSearch = this.removeWrongLastNames(resultOfAdditionalSearch, tokens.get(0)); + } + } else if (orgUserId != null) { + resultOfSearch = this.searchUsersByAttrs(attrUser); + } + if (resultOfAdditionalSearch != null) { + resultOfSearch.addAll(resultOfAdditionalSearch); + } + resultOfSearch = this.cutSearchResultToMaximumSize(resultOfSearch); + ObjectMapper mapper = new ObjectMapper(); + String result = "[]"; + try { + result = mapper.writeValueAsString(resultOfSearch); + } catch (JsonProcessingException e) { + logger.error(EELFLoggerDelegate.errorLogger, "searchUsersInPhoneBook Exception = " + EcompPortalUtils.getStackTrace(e)); + } + return result; + } + + private List searchUsersByAttrs(EPUser attrUser) { + List foundUsers = new ArrayList(); + try { + org.openecomp.portalsdk.core.command.support.SearchResult searchResult = this.ldapService.searchPost(attrUser, null, null, null, 0, 0, -1); + for (Object obj : searchResult) { + EPUser user = (EPUser) obj; + UserWithNameSurnameTitle foundUser = new UserWithNameSurnameTitle(user.getOrgUserId(), user.getFirstName(), user.getLastName(), user.getJobTitle()); + foundUsers.add(foundUser); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "searchInPhoneBookWithToken Exception = " + EcompPortalUtils.getStackTrace(e)); + } + return foundUsers; + } + + private List removeWrongFirstNames(List resultOfSearch, String firstName) { + firstName = firstName.toUpperCase(); + for (int i = resultOfSearch.size() - 1; i >= 0; i--) { + UserWithNameSurnameTitle user = resultOfSearch.get(i); + if ((user.firstName == null) || !user.firstName.toUpperCase().startsWith(firstName)) { + resultOfSearch.remove(i); + } + } + return resultOfSearch; + } + + private List removeWrongLastNames(List resultOfSearch, String lastName) { + lastName = lastName.toUpperCase(); + for (int i = resultOfSearch.size() - 1; i >= 0; i--) { + UserWithNameSurnameTitle user = resultOfSearch.get(i); + if ((user.lastName == null) || !user.lastName.toUpperCase().startsWith(lastName)) { + resultOfSearch.remove(i); + } + } + return resultOfSearch; + } + + private List cutSearchResultToMaximumSize(List resultOfSearch) { + for (int i = resultOfSearch.size() - 1; i >= maxSizeOfSearchResult; i--) { + resultOfSearch.remove(i); + } + return resultOfSearch; + } + + @Override + @SuppressWarnings("unchecked") + public EPUser searchUserByUserId(String orgUserId) { + EPUser user = null; + EPUser searchedUser = new EPUser(); + searchedUser.setOrgUserId(orgUserId); + try { + List searchResult = ldapService.searchPost(searchedUser, "", null, null, 0, -1, 1); + for (Object obj : searchResult) { + if (obj instanceof EPUser) { + user = (EPUser) obj; + // This assignment should be checked later! + user.setLoginId(orgUserId); + break; + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "searchUserBy orgUserId exception = " + EcompPortalUtils.getStackTrace(e)); + } + return user; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SharedContextService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SharedContextService.java new file mode 100644 index 00000000..0b1a6df7 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SharedContextService.java @@ -0,0 +1,98 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.SharedContext; + +/** + * Defines the methods exposed by the service that manages shared context + * objects in the database via Hibernate. + */ +public interface SharedContextService { + + /** + * Gets all shared context objects for the specified context ID. + * + * @return List of SharedContext objects + */ + List getSharedContexts(String contextId); + + /** + * Gets the shared context with the specified context ID and key. + * + * @param contextId + * Context ID; usually a session ID + * @param key + * Key for the key-value pair + * @return Value found in the database, null if any parameter is null or no + * shared context exists with that context ID - key pair. + */ + SharedContext getSharedContext(String contextId, String key); + + /** + * Creates a new shared context in the database with the specified context + * ID, key and value. + * + * @param context + * SharedContext object to save. + * @param key + * Key for the key-value pair. + * @param value + * Value for the key-value pair. + */ + void addSharedContext(String contextId, String key, String value); + + /** + * Saves the specified shared context to the database. + * + * @param context + * SharedContext object to save. + */ + void saveSharedContext(SharedContext context); + + /** + * Deletes the specified shared context from the database. + * + * @param context + * SharedContext object to delete. + */ + void deleteSharedContext(SharedContext context); + + /** + * Deletes all shared contexts with the specified context ID. + * + * @param contextId + * Context ID; usually a session ID + * @return number of shared-context objects deleted + */ + int deleteSharedContexts(String contextId); + + /** + * Deletes all shared contexts with a creation time that is older than the + * specified value. + * + * @param ageInSeconds + * Expiration threshold in seconds + */ + void expireSharedContexts(int ageInSeconds); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SharedContextServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SharedContextServiceImpl.java new file mode 100644 index 00000000..428f1354 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/SharedContextServiceImpl.java @@ -0,0 +1,177 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.hibernate.criterion.Criterion; +import org.hibernate.criterion.Restrictions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalapp.portal.domain.SharedContext; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; + +/** + * Implementation of the shared-context service that talks to the database. + */ +@Service("sharedContextService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class SharedContextServiceImpl implements SharedContextService { + + @Autowired + private DataAccessService dataAccessService; + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SharedContextServiceImpl.class); + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalsdk.core.service.SharedContextService# + * getSharedContexts() + */ + @Override + @SuppressWarnings("unchecked") + public List getSharedContexts(String contextId) { + List restrictionsList = new ArrayList(); + Criterion contextIdCrit = Restrictions.eq("context_id", contextId); + restrictionsList.add(contextIdCrit); + List contexts = (List) getDataAccessService().getList(SharedContext.class, null, + restrictionsList, null); + + return contexts; + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalsdk.core.service.SharedContextService# + * getSharedContext(java. lang.String, java.lang.String) + */ + @Override + public SharedContext getSharedContext(String contextId, String key) { + SharedContext context = null; + List restrictionsList = new ArrayList(); + Criterion contextIdCrit = Restrictions.eq("context_id", contextId); + Criterion keyCrit = Restrictions.eq("ckey", key); + restrictionsList.add(contextIdCrit); + restrictionsList.add(keyCrit); + @SuppressWarnings("unchecked") + List contexts = (List) getDataAccessService().getList(SharedContext.class, null, + restrictionsList, null); + if (contexts != null && contexts.size() == 1) + context = contexts.get(0); + + return context; + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalapp.portal.service.SharedContextService# + * addSharedContext(java.lang.String, java.lang.String, java.lang.String) + */ + @Override + public void addSharedContext(String contextId, String key, String value) { + SharedContext context = new SharedContext(contextId, key, value); + saveSharedContext(context); + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalsdk.core.service.SharedContextService# + * saveSharedContext(com. att.fusion.core.domain.SharedContext) + */ + @Override + public void saveSharedContext(SharedContext context) { + getDataAccessService().saveDomainObject(context, null); + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalsdk.core.service.SharedContextService# + * deleteSharedContext(com. att.fusion.core.domain.SharedContext) + */ + @Override + public void deleteSharedContext(SharedContext context) { + getDataAccessService().deleteDomainObject(context, null); + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalapp.portal.service.SharedContextService# + * deleteSharedContexts(java.lang.String) + */ + @Override + public int deleteSharedContexts(String contextId) { + // Uses an inefficient method to avoid a where clause + // that could be used to mount a SQL injection attack. + List contexts = getSharedContexts(contextId); + if (contexts == null) + return 0; + + logger.debug(EELFLoggerDelegate.debugLogger, "deleteSharedContexts: count is " + contexts.size()); + for (SharedContext sc : contexts) + deleteSharedContext(sc); + + return contexts.size(); + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalapp.portal.service.SharedContextService# + * expireSharedContexts(int) + */ + @Override + public void expireSharedContexts(int ageInSeconds) { + // Specific to the MySQL database. + // final String whereClause = " where create_time < ADDDATE(NOW(), + // INTERVAL - " + ageInSeconds + " SECOND)"; + final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date expiredDateTime = new Date(System.currentTimeMillis() - ageInSeconds * 1000); + logger.debug(EELFLoggerDelegate.debugLogger, + "expireSharedContexts: expire time is " + expiredDateTime.toString()); + final String whereClause = " create_time < '" + dateFormat.format(expiredDateTime) + "'"; + getDataAccessService().deleteDomainObjects(SharedContext.class, whereClause, null); + } + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + + public void setDataAccessService(DataAccessService dataAccessService) { + this.dataAccessService = dataAccessService; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserNotificationService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserNotificationService.java new file mode 100644 index 00000000..063fdf07 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserNotificationService.java @@ -0,0 +1,55 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EcompAppRole; +import org.openecomp.portalapp.portal.transport.EpNotificationItem; +import org.openecomp.portalapp.portal.transport.EpNotificationItemVO; +import org.openecomp.portalapp.portal.transport.EpRoleNotificationItem; + +public interface UserNotificationService { + + List getNotifications(Long userId); + + List getNotificationRoles(Long notificationId); + + List getAppRoleList(); + /** + * Marks the notification as viewed by the specified user. + * + * @param notificationId + * @param userId + */ + void setNotificationRead(Long notificationId, int userId); + + String saveNotification(EpNotificationItem notificationItem) throws Exception; + + List getNotificationHistoryVO(Long userId); + + List getAdminNotificationVOS(); + + List getUsersByOrgIds(List OrgIds); + + List getMessageRecipients(Long notificationId); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserNotificationServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserNotificationServiceImpl.java new file mode 100644 index 00000000..4017b2fc --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserNotificationServiceImpl.java @@ -0,0 +1,211 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.SessionFactory; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EPUserNotification; +import org.openecomp.portalapp.portal.domain.EcompAppRole; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.transport.EpNotificationItem; +import org.openecomp.portalapp.portal.transport.EpNotificationItemVO; +import org.openecomp.portalapp.portal.transport.EpRoleNotificationItem; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +@Service("userNotificationService") +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class UserNotificationServiceImpl implements UserNotificationService { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(FunctionalMenuServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + @Autowired + private SessionFactory sessionFactory; + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.UserNotificationService#getNotifications(java.lang.Long) + */ + @Override + public List getNotifications(Long userId) { + Map params = new HashMap(); + params.put("user_id", userId.toString()); + @SuppressWarnings("unchecked") + List notificationList = dataAccessService.executeNamedQuery("getNotifications", params, + null); + // set the roles to null for pure retrieval of all notifications + for (EpNotificationItem item : notificationList) { + item.setRoles(null); + } + return notificationList; + } + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.UserNotificationService#getNotificationHistoryVO(java.lang.Long) + */ + @Override + public List getNotificationHistoryVO(Long userId) { + Map params = new HashMap(); + params.put("user_id", userId.toString()); + @SuppressWarnings("unchecked") + List notificationList = dataAccessService.executeNamedQuery("getNotificationHistoryVO", + params, null); + return notificationList; + } + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.UserNotificationService#getAdminNotificationVOS() + */ + @Override + public List getAdminNotificationVOS() { + Map params = new HashMap(); + @SuppressWarnings("unchecked") + List notificationList = dataAccessService + .executeNamedQuery("getAdminNotificationHistoryVO", params, null); + return notificationList; + } + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.UserNotificationService#getNotificationRoles(java.lang.Long) + */ + @Override + public List getNotificationRoles(Long notificationId) { + Map params = new HashMap(); + params.put("notificationId", Long.toString(notificationId)); + @SuppressWarnings("unchecked") + List roleNotifList = dataAccessService.executeNamedQuery("getNotificationRoles", params, + null); + return roleNotifList; + } + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.UserNotificationService#getAppRoleList() + */ + @SuppressWarnings("unchecked") + @Override + public List getAppRoleList() { + List appRoleList = (List) dataAccessService.executeNamedQuery("getEpNotificationAppRoles", null, null); + return appRoleList; + } + + /* + * (non-Javadoc) + * + * @see org.openecomp.portalapp.portal.service.UserNotificationService# + * setNotificationsRead(java.lang.Long, int) + */ + @Override + public void setNotificationRead(Long notificationId, int userId) { + EPUserNotification userNotification = new EPUserNotification(); + userNotification.setNotificationId(notificationId); + userNotification.setUpdateTime(new Date()); + userNotification.setViewed("Y"); + userNotification.setUserId((long) userId); + getDataAccessService().saveDomainObject(userNotification, null); + } + + /* + * (non-Javadoc) + * @see org.openecomp.portalapp.portal.service.UserNotificationService#saveNotification(org.openecomp.portalapp.portal.transport.EpNotificationItem) + */ + @Override + public String saveNotification(EpNotificationItem notificationItem) throws Exception { + + // gather the roles + if (notificationItem.getRoleIds() != null && !notificationItem.getIsForAllRoles().equals("Y")) { + if (notificationItem.getRoles() == null) { + Set roleSet = new HashSet(); + notificationItem.setRoles(roleSet); + } + for (Long roleId : notificationItem.getRoleIds()) { + EpRoleNotificationItem roleItem = new EpRoleNotificationItem(); + roleItem.setNotificationId(notificationItem.getNotificationId()); + roleItem.setRoleId(roleId.intValue()); + notificationItem.getRoles().add(roleItem); + } + } + + + // for updates fetch roles and then save + if (notificationItem.getNotificationId() != null) { + EpNotificationItem updateNotificationItem = (EpNotificationItem) getDataAccessService() + .getDomainObject(EpNotificationItem.class, notificationItem.getNotificationId(), null); + notificationItem.setRoles(updateNotificationItem.getRoles()); + } + if(notificationItem.msgSource == null) + { + notificationItem.setMsgSource("EP"); + } + getDataAccessService().saveDomainObject(notificationItem, null); + return "" ; + + } + @Override + public List getUsersByOrgIds(List OrgIds) { + Map params = new HashMap(); + params.put("OrgIds", OrgIds); + @SuppressWarnings("unchecked") + List userList = dataAccessService.executeNamedQuery("getUsersByOrgIdsNotifications", params, null); + return userList; + } + + @Override + public List getMessageRecipients(Long notificationId) { + Map params = new HashMap<>(); + params.put("notificationId", Long.toString(notificationId)); + @SuppressWarnings("unchecked") + List activeUsers = dataAccessService.executeNamedQuery("messageRecipients", params, null); + return activeUsers; + } + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + + public void setDataAccessService(DataAccessService dataAccessService) { + this.dataAccessService = dataAccessService; + } + + public SessionFactory getSessionFactory() { + return sessionFactory; + } + + public void setSessionFactory(SessionFactory sessionFactory) { + this.sessionFactory = sessionFactory; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserRolesService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserRolesService.java new file mode 100644 index 00000000..1b9dfd04 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserRolesService.java @@ -0,0 +1,67 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.apache.cxf.transport.http.HTTPException; +import org.openecomp.portalapp.externalsystemapproval.model.ExternalSystemUser; +import org.openecomp.portalapp.portal.domain.EPRole; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EPUserApp; +import org.openecomp.portalapp.portal.domain.EPUserAppCatalogRoles; +import org.openecomp.portalapp.portal.domain.ExternalSystemAccess; +import org.openecomp.portalapp.portal.transport.AppWithRolesForUser; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.RoleInAppForUser; +import org.openecomp.portalapp.portal.transport.UserApplicationRoles; + +public interface UserRolesService { + + public List getAppRolesForUser(Long appId, String userId); + + public boolean setAppWithUserRoleStateForUser(EPUser user, AppWithRolesForUser newAppRolesForUser); + + public List getUsersFromAppEndpoint(Long appId) throws HTTPException; + + public List importRolesFromRemoteApplication(Long appId) throws HTTPException; + + /** + * Gets entries from the local fn_user_role table for the specified user and + * app. + * + * @param appId + * ID of row in fn_app + * @param userid + * ID of row in fn_user + * @return List of EPRole; empty if none found. + */ + public List getCachedAppRolesForUser(Long appId, Long userId); + + public FieldsValidator putUserAppRolesRequest(AppWithRolesForUser userAppRolesData, EPUser user); + + String setAppWithUserRoleStateForUser(ExternalSystemUser newAppRolesForUser); + + public List getUserAppCatalogRoles(EPUser user, String appName); + + public String updateRemoteUserProfile(String orgUserId, Long appId); + + public ExternalSystemAccess getExternalRequestAccess(); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserService.java new file mode 100644 index 00000000..2d8e9112 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserService.java @@ -0,0 +1,34 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.EPUser; + +public interface UserService { + + List getUserByUserId(String orgUserId); + + List getUserByFirstLastName(String firstName, String lastName); + + public String saveNewUser(EPUser newUser, String checkDuplicate) throws Exception; + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserServiceImpl.java new file mode 100644 index 00000000..4de2c497 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/UserServiceImpl.java @@ -0,0 +1,260 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalsdk.core.FusionObject.Utilities; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.util.CipherUtil; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service("userService") +@Transactional +public class UserServiceImpl implements UserService { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(UserServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + + public void setDataAccessService(DataAccessService dataAccessService) { + this.dataAccessService = dataAccessService; + } + + @SuppressWarnings("unchecked") + @Override + public List getUserByUserId(String userId) { + + if (SystemProperties.getProperty(SystemProperties.AUTHENTICATION_MECHANISM).trim().equalsIgnoreCase("OIDC")) { + List users = new ArrayList(); + List filterdUsers = new ArrayList(); + BufferedReader in = null; + HttpURLConnection con = null; + try { + String url = EPCommonSystemProperties.getProperty(EPCommonSystemProperties.AUTH_USER_SERVER); + URL obj = new URL(url); + + con = (HttpURLConnection) obj.openConnection(); + + // optional default is GET + con.setRequestMethod("GET"); + con.setConnectTimeout(3000); + con.setReadTimeout(8000); + + StringBuffer response = new StringBuffer(); + + in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8")); + String inputLine; + while ((inputLine = in.readLine()) != null) + response.append(inputLine); + JSONObject jObject = new JSONObject(response.toString()); // json + JSONArray jsonUsers = jObject.getJSONArray("response"); // get + // data + // object + for (int i = 0; i < jsonUsers.length(); i++) { + JSONObject eachObject = jsonUsers.getJSONObject(i); + EPUser eachUser = new EPUser(); + eachUser.setOrgUserId(eachObject.get("id").toString()); + eachUser.setFirstName(eachObject.get("givenName").toString()); + eachUser.setLastName(eachObject.get("familyName").toString()); + eachUser.setEmail(eachObject.get("email").toString()); + users.add(eachUser); + } + + for (int i = 0; i < users.size(); i++) { + if (Utilities.nvl(userId).length() > 0) { + if (!userId.equalsIgnoreCase(users.get(i).getOrgUserId())) { + continue; + } + } + filterdUsers.add(users.get(i)); + } + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getUserByUserId failed", e); + } finally { + try { + in.close(); + con.disconnect(); + } catch (IOException e) { + logger.error(EELFLoggerDelegate.errorLogger,"getUserByUserId failed to close", e); + } + } + + return filterdUsers; + + } else { + + List list = null; + StringBuffer criteria = new StringBuffer(); + criteria.append(" where org_user_id = '").append(userId).append("'"); + list = getDataAccessService().getList(EPUser.class, criteria.toString(), null, null); + return (list == null || list.size() == 0) ? null : list; + + } + + } + + @SuppressWarnings("unchecked") + @Override + public List getUserByFirstLastName(String firstName, String lastName) { + + if (!SystemProperties.getProperty(SystemProperties.AUTHENTICATION_MECHANISM).trim().equalsIgnoreCase("OIDC")) { + + List list = null; + StringBuffer criteria = new StringBuffer(); + if (firstName != null) + criteria.append(" where first_name = '").append(firstName).append("'"); + if (lastName != null) + criteria.append(" where last_name = '").append(lastName).append("'"); + list = getDataAccessService().getList(EPUser.class, criteria.toString(), null, null); + return (list == null || list.size() == 0) ? null : list; + + } else { + + List users = new ArrayList(); + List filterdUsers = new ArrayList(); + BufferedReader in = null; + HttpURLConnection con = null; + try { + String url = EPCommonSystemProperties.getProperty(EPCommonSystemProperties.AUTH_USER_SERVER); + URL obj = new URL(url); + + con = (HttpURLConnection) obj.openConnection(); + + // optional default is GET + con.setRequestMethod("GET"); + con.setConnectTimeout(3000); + con.setReadTimeout(8000); + + StringBuffer response = new StringBuffer(); + + in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8")); + String inputLine; + while ((inputLine = in.readLine()) != null) + response.append(inputLine); + JSONObject jObject = new JSONObject(response.toString()); // json + JSONArray jsonUsers = jObject.getJSONArray("response"); // get + // data + // object + for (int i = 0; i < jsonUsers.length(); i++) { + JSONObject eachObject = jsonUsers.getJSONObject(i); + EPUser eachUser = new EPUser(); + eachUser.setOrgUserId(eachObject.get("id").toString());// getString("id")); + eachUser.setFirstName(eachObject.get("givenName").toString()); + eachUser.setLastName(eachObject.get("familyName").toString()); + eachUser.setEmail(eachObject.get("email").toString()); + users.add(eachUser); + } + + for (int i = 0; i < users.size(); i++) { + + if (Utilities.nvl(firstName).length() > 0) { + if (!firstName.equalsIgnoreCase(users.get(i).getFirstName())) { + continue; + } + } + if (Utilities.nvl(lastName).length() > 0) { + if (!lastName.equalsIgnoreCase(users.get(i).getLastName())) { + continue; + } + } + + filterdUsers.add(users.get(i)); + + } + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "getUserByFirstLastName failed", e); + } finally { + try { + in.close(); + con.disconnect(); + } catch (IOException e) { + logger.error(EELFLoggerDelegate.errorLogger, "getUserByFirstLastName failed to close", e); + } + } + + return filterdUsers; + } + + } + + @SuppressWarnings("unchecked") + public String saveNewUser(EPUser newUser, String checkDuplicate) throws Exception { + + try { + + List list = null; + StringBuffer criteria = new StringBuffer(); + criteria.append(" where org_user_id = '").append(newUser.getLoginId()).append("'"); + list = getDataAccessService().getList(EPUser.class, criteria.toString(), null, null); + if (list == null || list.size() == 0) { + newUser.setActive(true); + newUser.setOrgUserId(newUser.getLoginId()); + newUser.setLoginPwd(CipherUtil.encrypt(newUser.getLoginPwd())); + getDataAccessService().saveDomainObject(newUser, null); + } else { + if (checkDuplicate.equals("Yes")) { + // userId already exist in database + return "Record already exist"; + } else { + + EPUser oldUser = (EPUser) list.get(0); + oldUser.setFirstName(newUser.getFirstName()); + oldUser.setLastName(newUser.getLastName()); + oldUser.setMiddleInitial(newUser.getMiddleInitial()); + if (!oldUser.getLoginPwd().equals(newUser.getLoginPwd())) + oldUser.setLoginPwd(CipherUtil.encrypt(newUser.getLoginPwd())); + else + oldUser.setLoginPwd(newUser.getLoginPwd()); + getDataAccessService().saveDomainObject(oldUser, null); + + } + + } + + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "saveNewUser failed", e); + throw new Exception(e); + } + return "success"; + }; + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetParameterService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetParameterService.java new file mode 100644 index 00000000..df01fb96 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetParameterService.java @@ -0,0 +1,64 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.WidgetCatalogParameter; + +public interface WidgetParameterService { + + /** + * Saves the specified user-defined widget parameters to the table ep_widget_parameters + * + * @param newParameter + */ + void saveUserParameter(WidgetCatalogParameter newParameter); + + /** + * Gets the specified user-defined widget parameter where paramId is used from all users + * + * @param paramId + * @return + * List of widget parameters + */ + List getUserParameterById(Long paramId); + + + /** + * Deletes the specified user-defined widget parameters from ep_widget_parameters table + * where paramId is used + * + * @param paramId + */ + void deleteUserParameterById(Long paramId); + + + /** + * Gets the specified user-defined widget parameter where paramId is used from the specified + * user with userId + * @param widgetId + * @param userId + * @param paramId + * @return + */ + WidgetCatalogParameter getUserParamById(Long widgetId, Long userId, Long paramId); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetParameterServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetParameterServiceImpl.java new file mode 100644 index 00000000..a3e9c61c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetParameterServiceImpl.java @@ -0,0 +1,92 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hibernate.SessionFactory; +import org.hibernate.criterion.Criterion; +import org.hibernate.criterion.Restrictions; +import org.openecomp.portalapp.portal.domain.WidgetCatalogParameter; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +@Service("widgetParameterService") +@EnableAspectJAutoProxy +@EPMetricsLog +public class WidgetParameterServiceImpl implements WidgetParameterService{ + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(WidgetParameterServiceImpl.class); + + @Autowired + private DataAccessService dataAccessService; + + @SuppressWarnings("unchecked") + @Override + public WidgetCatalogParameter getUserParamById(Long widgetId, Long userId, Long paramId) { + List restrictionsList = new ArrayList(); + Criterion widgetIdCrit = Restrictions.eq("widgetId", widgetId); + restrictionsList.add(widgetIdCrit); + Criterion attIdCrit = Restrictions.eq("userId", userId); + restrictionsList.add(attIdCrit); + Criterion paramIdCrit = Restrictions.eq("paramId", paramId); + restrictionsList.add(paramIdCrit); + + + WidgetCatalogParameter widgetParam = null; + List list = (List) dataAccessService + .getList(WidgetCatalogParameter.class, null, restrictionsList, null); + if(list.size() != 0) + widgetParam = list.get(0); + logger.debug(EELFLoggerDelegate.debugLogger, + "getUserParamById: widget parameters: " + widgetParam); + return widgetParam; + } + + @Override + public void saveUserParameter(WidgetCatalogParameter newParameter) { + dataAccessService.saveDomainObject(newParameter, null); + } + + @SuppressWarnings("unchecked") + @Override + public List getUserParameterById(Long paramId) { + List restrictionsList = new ArrayList(); + Criterion paramIdCrit = Restrictions.eq("paramId", paramId); + restrictionsList.add(paramIdCrit); + List list = (List) dataAccessService + .getList(WidgetCatalogParameter.class, null, restrictionsList, null); + return list; + } + + @Override + public void deleteUserParameterById(Long paramId) { + Map params = new HashMap(); + params.put("paramId", Long.toString(paramId)); + dataAccessService.executeNamedQuery("deleteWidgetCatalogParameter", params, null); + dataAccessService.executeNamedQuery("deleteMicroserviceParameterById", params, null); + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetService.java new file mode 100644 index 00000000..b44a19f3 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetService.java @@ -0,0 +1,36 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.List; + +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.OnboardingWidget; + +public interface WidgetService { + + List getOnboardingWidgets(EPUser user, boolean managed); + + FieldsValidator setOnboardingWidget(EPUser user, OnboardingWidget onboardingWidget); + + FieldsValidator deleteOnboardingWidget(EPUser user, Long onboardingWidgetId); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetServiceImpl.java new file mode 100644 index 00000000..484bef4f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/service/WidgetServiceImpl.java @@ -0,0 +1,276 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.service; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletResponse; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EPUserApp; +import org.openecomp.portalapp.portal.domain.Widget; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.transport.FieldsValidator; +import org.openecomp.portalapp.portal.transport.OnboardingWidget; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; + +@Service("widgetService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class WidgetServiceImpl implements WidgetService { + + private static final String baseSqlToken = " widget.WIDGET_ID, widget.WDG_NAME, widget.APP_ID, app.APP_NAME, widget.WDG_WIDTH, widget.WDG_HEIGHT, widget.WDG_URL" + + " from FN_WIDGET widget join FN_APP app ON widget.APP_ID = app.APP_ID"; + + private String validAppsFilter = ""; + + private Long LONG_ECOMP_APP_ID = 1L; + private Long ACCOUNT_ADMIN_ROLE_ID = 999L; + private static final Long DUBLICATED_FIELD_VALUE_ECOMP_ERROR = new Long(EPCommonSystemProperties.DUBLICATED_FIELD_VALUE_ECOMP_ERROR); + + private static final String urlField = "url"; + + private static final String nameField = "name"; + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(WidgetServiceImpl.class); + + @Autowired + AdminRolesService adminRolesService; + @Autowired + private SessionFactory sessionFactory; + @Autowired + private DataAccessService dataAccessService; + + @PostConstruct + private void init() { + try { + validAppsFilter = " AND app.ENABLED = 'Y' AND app.APP_ID != " + SystemProperties.getProperty(EPCommonSystemProperties.ECOMP_APP_ID); + ACCOUNT_ADMIN_ROLE_ID = Long.valueOf(SystemProperties.getProperty(EPCommonSystemProperties.ACCOUNT_ADMIN_ROLE_ID)); + LONG_ECOMP_APP_ID = Long.valueOf(SystemProperties.getProperty(EPCommonSystemProperties.ECOMP_APP_ID)); + } catch(Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } + } + + private String sqlWidgetsForAllApps() { + return "SELECT" + baseSqlToken + validAppsFilter; + } + + private String sqlWidgetsForAllAppsWhereUserIsAdmin(Long userId) { + return "SELECT" + baseSqlToken + " join FN_USER_ROLE ON FN_USER_ROLE.APP_ID = app.APP_ID where FN_USER_ROLE.USER_ID = " + userId + + " AND FN_USER_ROLE.ROLE_ID = " + ACCOUNT_ADMIN_ROLE_ID + validAppsFilter; + } + + private String sqlWidgetsForAllAppsWhereUserHasAnyRole(Long userId) { + return "SELECT DISTINCT" + baseSqlToken + " join FN_USER_ROLE ON FN_USER_ROLE.APP_ID = app.APP_ID where FN_USER_ROLE.USER_ID = " + + userId + validAppsFilter; + } + + @SuppressWarnings("unchecked") + @Override + public List getOnboardingWidgets(EPUser user, boolean managed) { + List onboardingWidgets = new ArrayList(); + String sql = null; + if (adminRolesService.isSuperAdmin(user)) { + sql = this.sqlWidgetsForAllApps(); + } else if (managed) { + if (adminRolesService.isAccountAdmin(user)) { + sql = this.sqlWidgetsForAllAppsWhereUserIsAdmin(user.getId()); + } + } else if (adminRolesService.isAccountAdmin(user) || adminRolesService.isUser(user)) { + sql = this.sqlWidgetsForAllAppsWhereUserHasAnyRole(user.getId()); + } + if (sql != null) { + onboardingWidgets = dataAccessService.executeSQLQuery(sql, OnboardingWidget.class, null); + } + return onboardingWidgets; + } + + private static final Object syncRests = new Object(); + + private boolean isUserAdminOfAppForWidget(boolean superAdmin, Long userId, Long appId) { + if (!superAdmin) { + @SuppressWarnings("unchecked") + List userRoles = dataAccessService.getList(EPUserApp.class, + " where userId = " + userId + " and role.id = " + ACCOUNT_ADMIN_ROLE_ID + " and app.id = " + appId, null, null); + return (userRoles.size() > 0); + } + return true; + } + + private void validateOnboardingWidget(OnboardingWidget onboardingWidget, FieldsValidator fieldsValidator) { + @SuppressWarnings("unchecked") + List widgets = dataAccessService.getList(Widget.class, + " where url = '" + onboardingWidget.url + "'" + " or name = '" + onboardingWidget.name + "'", null, null); + boolean dublicatedUrl = false; + boolean dublicatedName = false; + for (Widget widget : widgets) { + if (onboardingWidget.id != null && onboardingWidget.id.equals(widget.getId())) { + // widget should not be compared with itself + continue; + } + if (!dublicatedUrl && widget.getUrl().equals(onboardingWidget.url)) { + dublicatedUrl = true; + if (dublicatedName) { + break; + } + } + if (!dublicatedName && widget.getName().equalsIgnoreCase(onboardingWidget.name) && widget.getAppId().equals(onboardingWidget.appId)) { + dublicatedName = true; + if (dublicatedUrl) { + break; + } + } + } + if (dublicatedUrl || dublicatedName) { + if (dublicatedUrl) { + fieldsValidator.addProblematicFieldName(urlField); + } + if (dublicatedName) { + fieldsValidator.addProblematicFieldName(nameField); + } + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_CONFLICT); + fieldsValidator.errorCode = DUBLICATED_FIELD_VALUE_ECOMP_ERROR; + } + } + + private void applyOnboardingWidget(OnboardingWidget onboardingWidget, FieldsValidator fieldsValidator) { + boolean result = false; + Session localSession = null; + Transaction transaction = null; + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + Widget widget; + if (onboardingWidget.id == null) { + widget = new Widget(); + } else { + widget = (Widget) localSession.get(Widget.class, onboardingWidget.id); + } + widget.setAppId(onboardingWidget.appId); + widget.setName(onboardingWidget.name); + widget.setWidth(onboardingWidget.width); + widget.setHeight(onboardingWidget.height); + widget.setUrl(onboardingWidget.url); + localSession.saveOrUpdate(widget); + transaction.commit(); + result = true; + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + EcompPortalUtils.rollbackTransaction(transaction, "applyOnboardingWidget rollback, exception = " + e); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "applyOnboardingWidget"); + } + if (!result) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + private FieldsValidator updateOrSaveWidget(boolean superAdmin, Long userId, OnboardingWidget onboardingWidget) { + FieldsValidator fieldsValidator = new FieldsValidator(); + if (!this.isUserAdminOfAppForWidget(superAdmin, userId, onboardingWidget.appId)) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_FORBIDDEN); + return fieldsValidator; + } + synchronized (syncRests) { + // onboardingWidget.id is null for POST and not null for PUT + if (onboardingWidget.id == null) { + this.validateOnboardingWidget(onboardingWidget, fieldsValidator); + } else { + Widget widget = (Widget) dataAccessService.getDomainObject(Widget.class, onboardingWidget.id, null); + if (widget == null || widget.getId() == null) { + // Widget not found + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_NOT_FOUND); + return fieldsValidator; + } + this.validateOnboardingWidget(onboardingWidget, fieldsValidator); + } + if (fieldsValidator.httpStatusCode.intValue() == HttpServletResponse.SC_OK) { + this.applyOnboardingWidget(onboardingWidget, fieldsValidator); + } + } + return fieldsValidator; + } + + @Override + public FieldsValidator setOnboardingWidget(EPUser user, OnboardingWidget onboardingWidget) { + if (onboardingWidget.name.length() == 0 || onboardingWidget.url.length() == 0 || onboardingWidget.appId == null + || onboardingWidget.appId.equals(LONG_ECOMP_APP_ID) || onboardingWidget.width.intValue() <= 0 || onboardingWidget.height.intValue() <= 0) { + if (onboardingWidget.appId.equals(LONG_ECOMP_APP_ID)) { + // logger.error("Alarm!!! Security breach attempt on user " + user.getFullName() + ", userId = " + user.getUserId()); + } + FieldsValidator fieldsValidator = new FieldsValidator(); + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_BAD_REQUEST); + return fieldsValidator; + } + return this.updateOrSaveWidget(adminRolesService.isSuperAdmin(user), user.getId(), onboardingWidget); + } + + @Override + public FieldsValidator deleteOnboardingWidget(EPUser user, Long onboardingWidgetId) { + FieldsValidator fieldsValidator = new FieldsValidator(); + synchronized (syncRests) { + Widget widget = (Widget) dataAccessService.getDomainObject(Widget.class, onboardingWidgetId, null); + if (widget != null && widget.getId() != null) { // widget exists + if (!this.isUserAdminOfAppForWidget(adminRolesService.isSuperAdmin(user), user.getId(), widget.getAppId())) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_FORBIDDEN); + } else { + boolean result = false; + Session localSession = null; + Transaction transaction = null; + try { + localSession = sessionFactory.openSession(); + transaction = localSession.beginTransaction(); + localSession.delete(localSession.get(Widget.class, onboardingWidgetId)); + transaction.commit(); + result = true; + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoSystemError, e); + EcompPortalUtils.rollbackTransaction(transaction, "deleteOnboardingWidget rollback, exception = " + e); + } finally { + EcompPortalUtils.closeLocalSession(localSession, "deleteOnboardingWidget"); + } + if (!result) { + fieldsValidator.httpStatusCode = new Long(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + } + } + return fieldsValidator; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AllAppsWithRolesForUser.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AllAppsWithRolesForUser.java new file mode 100644 index 00000000..da776ded --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AllAppsWithRolesForUser.java @@ -0,0 +1,40 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.List; + +public class AllAppsWithRolesForUser { + + public String orgUserId; + + public List apps; + + public static class AppWithUserRoles { + + public Long appId; + + public String appName; + + public List appRoles; + + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/Analytics.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/Analytics.java new file mode 100644 index 00000000..adc5ad2b --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/Analytics.java @@ -0,0 +1,66 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Analytics { + + private String action; + private String page; + private String function; + private String userId; + private String type; + + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + public String getAction() { + return action; + } + public void setAction(String action) { + this.action = action; + } + public String getPage() { + return page; + } + public void setPage(String page) { + this.page = page; + } + public String getFunction() { + return function; + } + public void setFunction(String function) { + this.function = function; + } + public String getUserId() { + return userId; + } + public void setUserId(String userId) { + this.userId = userId; + } + + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppCatalogPersonalization.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppCatalogPersonalization.java new file mode 100644 index 00000000..7c24917b --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppCatalogPersonalization.java @@ -0,0 +1,94 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +/** + * Model for the object PUT to the controller when the user takes an action on + * an application in the catalog. + */ +public class AppCatalogPersonalization { + + public Long appId; + public Boolean select; + public Boolean pending; + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public Boolean getSelect() { + return select; + } + + public void setSelect(Boolean select) { + this.select = select; + } + + public Boolean getPending() { + return pending; + } + + public void setPending(Boolean pending) { + this.pending = pending; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((appId == null) ? 0 : appId.hashCode()); + result = prime * result + ((pending == null) ? 0 : pending.hashCode()); + result = prime * result + ((select == null) ? 0 : select.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AppCatalogPersonalization other = (AppCatalogPersonalization) obj; + if (appId == null) { + if (other.appId != null) + return false; + } else if (!appId.equals(other.appId)) + return false; + if (pending == null) { + if (other.pending != null) + return false; + } else if (!pending.equals(other.pending)) + return false; + if (select == null) { + if (other.select != null) + return false; + } else if (!select.equals(other.select)) + return false; + return true; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppNameIdIsAdmin.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppNameIdIsAdmin.java new file mode 100644 index 00000000..484fffad --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppNameIdIsAdmin.java @@ -0,0 +1,114 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +public class AppNameIdIsAdmin { + + public Long id; + + public String appName; + + public Boolean isAdmin; + + public Boolean restrictedApp; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public Boolean getIsAdmin() { + return isAdmin; + } + + public void setIsAdmin(Boolean isAdmin) { + this.isAdmin = isAdmin; + } + + public Boolean getRestrictedApp() { + return restrictedApp; + } + + public void setRestrictedApp(Boolean restrictedApp) { + this.restrictedApp = restrictedApp; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((appName == null) ? 0 : appName.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((isAdmin == null) ? 0 : isAdmin.hashCode()); + result = prime * result + ((restrictedApp == null) ? 0 : restrictedApp.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AppNameIdIsAdmin other = (AppNameIdIsAdmin) obj; + if (appName == null) { + if (other.appName != null) + return false; + } else if (!appName.equals(other.appName)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (isAdmin == null) { + if (other.isAdmin != null) + return false; + } else if (!isAdmin.equals(other.isAdmin)) + return false; + if (restrictedApp == null) { + if (other.restrictedApp != null) + return false; + } else if (!restrictedApp.equals(other.restrictedApp)) + return false; + return true; + } + + @Override + public String toString() { + return "AppNameIdIsAdmin [id=" + id + ", appName=" + appName + ", isAdmin=" + isAdmin + ", restrictedApp=" + + restrictedApp + "]"; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppWithRolesForUser.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppWithRolesForUser.java new file mode 100644 index 00000000..4e37ee00 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppWithRolesForUser.java @@ -0,0 +1,73 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.List; + +public class AppWithRolesForUser { + + public String orgUserId; + + public Long appId; + + public String appName; + + public List appRoles; + + public String getOrgUserId() { + return orgUserId; + } + + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public List getAppRoles() { + return appRoles; + } + + public void setAppRoles(List appRoles) { + this.appRoles = appRoles; + } + + @Override + public String toString() { + return "AppWithRolesForUser [orgUserId=" + orgUserId + ", appId=" + appId + ", appName=" + appName + + ", appRoles=" + appRoles + "]"; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppsListWithAdminRole.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppsListWithAdminRole.java new file mode 100644 index 00000000..0000e2a7 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/AppsListWithAdminRole.java @@ -0,0 +1,86 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.ArrayList; + +public class AppsListWithAdminRole { + + public String orgUserId; + + public ArrayList appsRoles; + + public AppsListWithAdminRole() { + appsRoles = new ArrayList(); + } + + public String getOrgUserId() { + return orgUserId; + } + + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } + + public ArrayList getAppsRoles() { + return appsRoles; + } + + public void setAppsRoles(ArrayList appsRoles) { + this.appsRoles = appsRoles; + } + + @Override + public String toString() { + return "AppsListWithAdminRole [orgUserId=" + orgUserId + ", appsRoles=" + appsRoles + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((appsRoles == null) ? 0 : appsRoles.hashCode()); + result = prime * result + ((orgUserId == null) ? 0 : orgUserId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AppsListWithAdminRole other = (AppsListWithAdminRole) obj; + if (appsRoles == null) { + if (other.appsRoles != null) + return false; + } else if (!appsRoles.equals(other.appsRoles)) + return false; + if (orgUserId == null) { + if (other.orgUserId != null) + return false; + } else if (!orgUserId.equals(other.orgUserId)) + return false; + return true; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/BusinessCardApplicationRole.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/BusinessCardApplicationRole.java new file mode 100644 index 00000000..47658786 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/BusinessCardApplicationRole.java @@ -0,0 +1,108 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@Entity +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BusinessCardApplicationRole implements Serializable{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + + public BusinessCardApplicationRole() { + } + + + @Id + @Column(name="app_name") + private String appName; + + @Id + @Column(name="role_name") + private String roleName; + + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + @Override + public String toString() { + return "BusinessCardUserApplicationRoles [appName=" + appName + ", roleName=" + roleName + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((appName == null) ? 0 : appName.hashCode()); + result = prime * result + ((roleName == null) ? 0 : roleName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BusinessCardApplicationRole other = (BusinessCardApplicationRole) obj; + if (appName == null) { + if (other.appName != null) + return false; + } else if (!appName.equals(other.appName)) + return false; + if (roleName == null) { + if (other.roleName != null) + return false; + } else if (!roleName.equals(other.roleName)) + return false; + return true; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/BusinessCardApplicationRolesList.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/BusinessCardApplicationRolesList.java new file mode 100644 index 00000000..15c3aff2 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/BusinessCardApplicationRolesList.java @@ -0,0 +1,86 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BusinessCardApplicationRolesList { + + public BusinessCardApplicationRolesList(){} + + private String appName; + private List roleNames; + + + public String getAppName() { + return appName; + } + public void setAppName(String appName) { + this.appName = appName; + } + public List getRoleNames() { + return roleNames; + } + public void setRoleNames(List roleNames) { + this.roleNames = roleNames; + } + public BusinessCardApplicationRolesList(String appName, List roleNames) { + super(); + this.appName = appName; + this.roleNames = roleNames; + } + @Override + public String toString() { + return "BusinessCardUserAppRoles [appName=" + appName + ", roleNames=" + roleNames + "]"; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((appName == null) ? 0 : appName.hashCode()); + result = prime * result + ((roleNames == null) ? 0 : roleNames.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BusinessCardApplicationRolesList other = (BusinessCardApplicationRolesList) obj; + if (appName == null) { + if (other.appName != null) + return false; + } else if (!appName.equals(other.appName)) + return false; + if (roleNames == null) { + if (other.roleNames != null) + return false; + } else if (!roleNames.equals(other.roleNames)) + return false; + return true; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/CommonWidget.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/CommonWidget.java new file mode 100644 index 00000000..da2ae4ea --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/CommonWidget.java @@ -0,0 +1,139 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * This is to handle portal admins + * @author aw3218 + */ +@Entity +@Table(name="fn_common_widget_data") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CommonWidget extends DomainVo{ + + private static final long serialVersionUID = 7897021982887364557L; + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + @Column(name = "id") + private Long id; + + @Column(name = "category") + public String category; + + @Column(name = "href") + public String href; + + @Column(name = "title") + public String title; + + @Column(name = "content") + public String content; + + @Column(name = "event_date") + public String eventDate; + + @Column(name = "sort_order") + public Integer sortOrder; + + + public CommonWidget(){ + + } + + public CommonWidget(String category, String href, String title, String content, String eventDate, Integer sortOrder){ + this.category = category; + this.href = href; + this.title = title; + this.content = content; + this.eventDate = eventDate; + this.sortOrder = sortOrder; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getHref() { + return href; + } + + public void setHref(String href) { + this.href = href; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public void setSortOrder(Integer sortOrder) { + this.sortOrder = sortOrder; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getEventDate() { + return eventDate; + } + + public void setEventDate(String eventDate) { + this.eventDate = eventDate; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/CommonWidgetMeta.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/CommonWidgetMeta.java new file mode 100644 index 00000000..ad71b081 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/CommonWidgetMeta.java @@ -0,0 +1,50 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.List; + +public class CommonWidgetMeta { + + private String category; + private List items; + + public CommonWidgetMeta(){ + + } + + public CommonWidgetMeta(String category, List items){ + this.category = category; + this.items = items; + } + + public String getCategory() { + return category; + } + public void setCategory(String category) { + this.category = category; + } + public List getItems() { + return items; + } + public void setItems(List items) { + this.items = items; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPAppsManualPreference.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPAppsManualPreference.java new file mode 100644 index 00000000..658ef51e --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPAppsManualPreference.java @@ -0,0 +1,114 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +public class EPAppsManualPreference { + + private Long appid; + private int col; + private String headerText; + private String imageLink; + private int order; + private boolean restrictedApp; + private int row; + private int sizeX; + private int sizeY; + private String subHeaderText; + private String url; + private boolean addRemoveApps; + + + public boolean isAddRemoveApps() { + return addRemoveApps; + } + public void setAddRemoveApps(boolean addRemoveApps) { + this.addRemoveApps = addRemoveApps; + } + public Long getAppid() { + return appid; + } + public void setAppid(Long appid) { + this.appid = appid; + } + public int getCol() { + return col; + } + public void setCol(int col) { + this.col = col; + } + public String getHeaderText() { + return headerText; + } + public void setHeaderText(String headerText) { + this.headerText = headerText; + } + public String getImageLink() { + return imageLink; + } + public void setImageLink(String imageLink) { + this.imageLink = imageLink; + } + public int getOrder() { + return order; + } + public void setOrder(int order) { + this.order = order; + } + public boolean isRestrictedApp() { + return restrictedApp; + } + public void setRestrictedApp(boolean restrictedApp) { + this.restrictedApp = restrictedApp; + } + public int getRow() { + return row; + } + public void setRow(int row) { + this.row = row; + } + public int getSizeX() { + return sizeX; + } + public void setSizeX(int sizeX) { + this.sizeX = sizeX; + } + public int getSizeY() { + return sizeY; + } + public void setSizeY(int sizeY) { + this.sizeY = sizeY; + } + public String getSubHeaderText() { + return subHeaderText; + } + public void setSubHeaderText(String subHeaderText) { + this.subHeaderText = subHeaderText; + } + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + + public boolean isValid(){ + return appid != null; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPAppsSortPreference.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPAppsSortPreference.java new file mode 100644 index 00000000..8aa0860c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPAppsSortPreference.java @@ -0,0 +1,47 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +public class EPAppsSortPreference { + + private int index; + private String value; + private String title; + + public int getIndex() { + return index; + } + public void setIndex(int index) { + this.index = index; + } + public String getValue() { + return value; + } + public void setValue(String value) { + this.value = value; + } + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPDeleteAppsManualSortPref.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPDeleteAppsManualSortPref.java new file mode 100644 index 00000000..700b74b1 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPDeleteAppsManualSortPref.java @@ -0,0 +1,51 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +public class EPDeleteAppsManualSortPref { + private Long appId; + private boolean select; + private boolean pending; + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public boolean isSelect() { + return select; + } + + public void setSelect(boolean select) { + this.select = select; + } + + public boolean isPending() { + return pending; + } + + public void setPending(boolean pending) { + this.pending = pending; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPWidgetsSortPreference.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPWidgetsSortPreference.java new file mode 100644 index 00000000..3fadf1a4 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EPWidgetsSortPreference.java @@ -0,0 +1,92 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.List; + +public class EPWidgetsSortPreference { + + private int SizeX; + private int SizeY; + private String headerText; + private String url; + private Long widgetid; + private List attrb; + private String widgetIdentifier; + private int row; + private int col; + + public String getWidgetIdentifier() { + return widgetIdentifier; + } + public List getAttrb() { + return attrb; + } + public void setAttrb(List attrb) { + this.attrb = attrb; + } + public void setWidgetIdentifier(String widgetIdentifier) { + this.widgetIdentifier = widgetIdentifier; + } + public int getSizeX() { + return SizeX; + } + public void setSizeX(int sizeX) { + SizeX = sizeX; + } + public int getSizeY() { + return SizeY; + } + public void setSizeY(int sizeY) { + SizeY = sizeY; + } + public String getHeaderText() { + return headerText; + } + public void setHeaderText(String headerText) { + this.headerText = headerText; + } + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + public Long getWidgetid() { + return widgetid; + } + public void setWidgetid(Long widgetid) { + this.widgetid = widgetid; + } + public int getRow() { + return row; + } + public void setRow(int row) { + this.row = row; + } + public int getCol() { + return col; + } + public void setCol(int col) { + this.col = col; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItem.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItem.java new file mode 100644 index 00000000..94fbe59a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItem.java @@ -0,0 +1,328 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.Date; +import java.util.List; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Transient; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + + +/** + * This is to handle notifications in notification PopUp + */ + + +@Entity +@Table(name = "ep_notification") +public class EpNotificationItem extends DomainVo { + public EpNotificationItem() { + }; + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "notification_ID") + public Long notificationId; + + @Column(name = "is_for_online_users") + public String isForOnlineUsers; + + @Column(name = "is_for_all_roles") + public String isForAllRoles; + + @Column(name = "active_YN") + public String activeYn; + + @Column(name = "msg_header") + public String msgHeader; + + @Column(name = "msg_description") + public String msgDescription; + + @Column(name = "msg_source") + public String msgSource; + + @Column(name = "start_time") + public Date startTime; + + @Column(name = "end_time") + public Date endTime; + + @Column(name = "priority") + public Long priority; + + @Column(name = "creator_ID") + public Long creatorId; + + @Column(name = "created_date") + public Date createdDate; + + + @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}, orphanRemoval = true) + @JoinColumn(name="notification_ID") + private Set roles; + + @Transient + private List roleIds; + + public Long getNotificationId() { + return notificationId; + } + + public void setNotificationId(Long notificationId) { + this.notificationId = notificationId; + } + + public String getIsForOnlineUsers() { + return isForOnlineUsers; + } + + public void setIsForOnlineUsers(String isForOnlineUsers) { + this.isForOnlineUsers = isForOnlineUsers; + } + + public String getIsForAllRoles() { + return isForAllRoles; + } + + public void setIsForAllRoles(String isForAllRoles) { + this.isForAllRoles = isForAllRoles; + } + + public String getActiveYn() { + return activeYn; + } + + public void setActiveYn(String activeYn) { + this.activeYn = activeYn; + } + + public String getMsgHeader() { + return msgHeader; + } + + public void setMsgHeader(String msgHeader) { + this.msgHeader = msgHeader; + } + + public String getMsgDescription() { + return msgDescription; + } + + public void setMsgDescription(String msgDescription) { + this.msgDescription = msgDescription; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Long getPriority() { + return priority; + } + + public void setPriority(Long priority) { + this.priority = priority; + } + + public Long getCreatorId() { + return creatorId; + } + + public void setCreatorId(Long creatorId) { + this.creatorId = creatorId; + } + + public Date getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Date createdDate) { + this.createdDate = createdDate; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } + + public List getRoleIds() { + return roleIds; + } + + public void setRoleIds(List roleIds) { + this.roleIds = roleIds; + } + + public String getMsgSource() { + return msgSource; + } + + public void setMsgSource(String msgSource) { + this.msgSource = msgSource; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((activeYn == null) ? 0 : activeYn.hashCode()); + result = prime * result + ((createdDate == null) ? 0 : createdDate.hashCode()); + result = prime * result + ((creatorId == null) ? 0 : creatorId.hashCode()); + result = prime * result + ((endTime == null) ? 0 : endTime.hashCode()); + result = prime * result + ((isForAllRoles == null) ? 0 : isForAllRoles.hashCode()); + result = prime * result + ((isForOnlineUsers == null) ? 0 : isForOnlineUsers.hashCode()); + result = prime * result + ((msgDescription == null) ? 0 : msgDescription.hashCode()); + result = prime * result + ((msgHeader == null) ? 0 : msgHeader.hashCode()); + result = prime * result + ((msgSource == null) ? 0 : msgSource.hashCode()); + result = prime * result + ((notificationId == null) ? 0 : notificationId.hashCode()); + result = prime * result + ((priority == null) ? 0 : priority.hashCode()); + result = prime * result + ((roleIds == null) ? 0 : roleIds.hashCode()); + result = prime * result + ((roles == null) ? 0 : roles.hashCode()); + result = prime * result + ((startTime == null) ? 0 : startTime.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EpNotificationItem other = (EpNotificationItem) obj; + if (activeYn == null) { + if (other.activeYn != null) + return false; + } else if (!activeYn.equals(other.activeYn)) + return false; + if (createdDate == null) { + if (other.createdDate != null) + return false; + } else if (!createdDate.equals(other.createdDate)) + return false; + if (creatorId == null) { + if (other.creatorId != null) + return false; + } else if (!creatorId.equals(other.creatorId)) + return false; + if (endTime == null) { + if (other.endTime != null) + return false; + } else if (!endTime.equals(other.endTime)) + return false; + if (isForAllRoles == null) { + if (other.isForAllRoles != null) + return false; + } else if (!isForAllRoles.equals(other.isForAllRoles)) + return false; + if (isForOnlineUsers == null) { + if (other.isForOnlineUsers != null) + return false; + } else if (!isForOnlineUsers.equals(other.isForOnlineUsers)) + return false; + if (msgDescription == null) { + if (other.msgDescription != null) + return false; + } else if (!msgDescription.equals(other.msgDescription)) + return false; + if (msgHeader == null) { + if (other.msgHeader != null) + return false; + } else if (!msgHeader.equals(other.msgHeader)) + return false; + if (msgSource == null) { + if (other.msgSource != null) + return false; + } else if (!msgSource.equals(other.msgSource)) + return false; + if (notificationId == null) { + if (other.notificationId != null) + return false; + } else if (!notificationId.equals(other.notificationId)) + return false; + if (priority == null) { + if (other.priority != null) + return false; + } else if (!priority.equals(other.priority)) + return false; + if (roleIds == null) { + if (other.roleIds != null) + return false; + } else if (!roleIds.equals(other.roleIds)) + return false; + if (roles == null) { + if (other.roles != null) + return false; + } else if (!roles.equals(other.roles)) + return false; + if (startTime == null) { + if (other.startTime != null) + return false; + } else if (!startTime.equals(other.startTime)) + return false; + return true; + } + + @Override + public String toString() { + return "EpNotificationItem [notificationId=" + notificationId + ", isForOnlineUsers=" + isForOnlineUsers + + ", isForAllRoles=" + isForAllRoles + ", activeYn=" + activeYn + ", msgHeader=" + msgHeader + + ", msgDescription=" + msgDescription + ", msgSource=" + msgSource + ", startTime=" + startTime + + ", endTime=" + endTime + ", priority=" + priority + ", creatorId=" + creatorId + ", createdDate=" + + createdDate + ", roles=" + roles + ", roleIds=" + roleIds + "]"; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItemExtVO.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItemExtVO.java new file mode 100644 index 00000000..4a3dc80d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItemExtVO.java @@ -0,0 +1,119 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class EpNotificationItemExtVO { + + private Character activeYn; + + private String msgHeader; + + private String msgDescription; + + private Date startTime; + + private Date endTime; + + private Integer priority; + + private Integer creatorId; + + private Date createdDate; + + private String loginId; + + public Character getActiveYn() { + return activeYn; + } + + public void setActiveYn(Character activeYn) { + this.activeYn = activeYn; + } + + public String getMsgHeader() { + return msgHeader; + } + + public void setMsgHeader(String msgHeader) { + this.msgHeader = msgHeader; + } + + public String getMsgDescription() { + return msgDescription; + } + + public void setMsgDescription(String msgDescription) { + this.msgDescription = msgDescription; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Integer getPriority() { + return priority; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + public Integer getCreatorId() { + return creatorId; + } + + public void setCreatorId(Integer creatorId) { + this.creatorId = creatorId; + } + + public Date getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Date createdDate) { + this.createdDate = createdDate; + } + + public String getLoginId() { + return loginId; + } + + public void setLoginId(String loginId) { + this.loginId = loginId; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItemVO.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItemVO.java new file mode 100644 index 00000000..d22a4b79 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpNotificationItemVO.java @@ -0,0 +1,191 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +/** + * This is to handle notifications in user notifications and in notification history + */ + +/** + * POJO that models a single notification with the org user ID (not integer + * user_id). + */ +@Entity +public class EpNotificationItemVO extends DomainVo { + + private static final long serialVersionUID = 9095479701352339201L; + + @Id + private Integer notificationId; + + private Character isForOnlineUsers; + + private Character isForAllRoles; + + private Character activeYn; + + private String msgHeader; + + private String msgDescription; + + private String msgSource; + + private Date startTime; + + private Date endTime; + + private Integer priority; + + private Integer creatorId; + + private Date createdDate; + + private String loginId; + + /** + * Answers whether the notification is expired. + * + * @return true if the end time is past the current time, else false. + */ + public boolean isExpired() { + boolean result = false; + if (endTime != null) { + int expired = endTime.compareTo(new Date()); + result = (expired == -1) ? true : false; + } + return result; + } + + public String getLoginId() { + return loginId; + } + + public void setLoginId(String loginId) { + this.loginId = loginId; + } + + public Integer getNotificationId() { + return notificationId; + } + + public void setNotificationId(Integer notificationId) { + this.notificationId = notificationId; + } + + public Character getIsForOnlineUsers() { + return isForOnlineUsers; + } + + public void setIsForOnlineUsers(Character isForOnlineUsers) { + this.isForOnlineUsers = isForOnlineUsers; + } + + public Character getIsForAllRoles() { + return isForAllRoles; + } + + public void setIsForAllRoles(Character isForAllRoles) { + this.isForAllRoles = isForAllRoles; + } + + public Character getActiveYn() { + return activeYn; + } + + public void setActiveYn(Character activeYn) { + this.activeYn = activeYn; + } + + public String getMsgHeader() { + return msgHeader; + } + + public void setMsgHeader(String msgHeader) { + this.msgHeader = msgHeader; + } + + public String getMsgDescription() { + return msgDescription; + } + + public void setMsgDescription(String msgDescription) { + this.msgDescription = msgDescription; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Integer getPriority() { + return priority; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + public Integer getCreatorId() { + return creatorId; + } + + public void setCreatorId(Integer creatorId) { + this.creatorId = creatorId; + } + + public Date getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Date createdDate) { + this.createdDate = createdDate; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + + public String getMsgSource() { + return msgSource; + } + + public void setMsgSource(String msgSource) { + this.msgSource = msgSource; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpRoleNotificationItem.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpRoleNotificationItem.java new file mode 100644 index 00000000..66840ed7 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EpRoleNotificationItem.java @@ -0,0 +1,90 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; + +@Entity +@Table(name="ep_role_notification") +public class EpRoleNotificationItem extends DomainVo { + public EpRoleNotificationItem(){}; + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name = "ID") + public Long id; + + @Column(name = "notification_ID") + public Long notificationId; + + @Column(name = "role_ID") + public Integer roleId; + + @Column(name = "recv_user_id") + public Integer RecvUserId; + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getNotificationId() { + return notificationId; + } + + public void setNotificationId(Long notificationId) { + this.notificationId = notificationId; + } + + public Integer getRoleId() { + return roleId; + } + + public void setRoleId(Integer roleId) { + this.roleId = roleId; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + public Integer getRecvUserId() { + return RecvUserId; + } + + public void setRecvUserId(Integer recvUserId) { + RecvUserId = recvUserId; + } + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EventWidget.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EventWidget.java new file mode 100644 index 00000000..569dc138 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EventWidget.java @@ -0,0 +1,138 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +/*package org.openecomp.portalapp.portal.transport; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.openecomp.portalsdk.core.domain.support.DomainVo; +import com.fasterxml.jackson.annotation.JsonInclude; + +*//** + * This is to handle portal admins + * @author aw3218 + *//* +@Entity +@Table(name="fn_event_widget_data") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class EventWidget extends DomainVo{ + + *//** + * + *//* + private static final long serialVersionUID = -2784849102886421352L; + + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + @Column(name = "id") + private Long id; + + @Column(name = "category") + private String category; + + @Column(name = "title") + private String title; + + @Column(name = "href") + private String href; + + @Column(name = "content") + private String content; + + @Column(name = "month_val") + private String mon; + + @Column(name = "day_val") + private Integer day; + + @Column(name = "year_val") + private Integer year; + + @Column(name = "sort_order") + private Integer sortOrder; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getHref() { + return href; + } + + public void setHref(String href) { + this.href = href; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getMon() { + return mon; + } + + public void setMonth(String mon) { + this.mon = mon; + } + + public Integer getDay() { + return day; + } + + public void setDay(Integer day) { + this.day = day; + } + + public Integer getYear() { + return year; + } + + public void setYear(Integer year) { + this.year = year; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + +} +*/ diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EventWidgetMeta.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EventWidgetMeta.java new file mode 100644 index 00000000..2182d7b3 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/EventWidgetMeta.java @@ -0,0 +1,51 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +/*package org.openecomp.portalapp.portal.transport; + +import java.util.List; + +public class EventWidgetMeta { + + private String category; + private List items; + + public EventWidgetMeta(){ + + } + + public EventWidgetMeta(String category, List items){ + this.category = category; + this.items = items; + } + + public String getCategory() { + return category; + } + public void setCategory(String category) { + this.category = category; + } + public List getItems() { + return items; + } + public void setItems(List items) { + this.items = items; + } +} +*/ diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FavoritesFunctionalMenuItem.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FavoritesFunctionalMenuItem.java new file mode 100644 index 00000000..d90dfc1e --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FavoritesFunctionalMenuItem.java @@ -0,0 +1,45 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + + +/** + * This is to handle functional menu favorites + */ +@Entity +@Table(name="fn_menu_favorites") +public class FavoritesFunctionalMenuItem implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @Column(name = "user_id") + public Long userId; + + @Id + @Column(name = "menu_id") + public Long menuId; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FavoritesFunctionalMenuItemJson.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FavoritesFunctionalMenuItemJson.java new file mode 100644 index 00000000..e469e0f9 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FavoritesFunctionalMenuItemJson.java @@ -0,0 +1,57 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Transient; + + + +/** + * This is to handle functional menu favorites + */ +@Entity +@Table(name="fn_menu_favorites") +public class FavoritesFunctionalMenuItemJson implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @Column(name = "user_id") + public Long userId; + + @Id + @Column(name = "menu_id") + public Long menuId; + + @Column(name = "text") + public String text; + + @Column(name = "url") + public String url; + + @Transient + public Boolean restrictedApp; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FieldsValidator.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FieldsValidator.java new file mode 100644 index 00000000..ab51b7b1 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FieldsValidator.java @@ -0,0 +1,125 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +public class FieldsValidator { + + public Long httpStatusCode = new Long(HttpServletResponse.SC_OK); + + public Long errorCode; + + public class FieldName { + + public String name; + + public FieldName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } + + + public List fields = new ArrayList(); + + public void addProblematicFieldName(String name) { + fields.add(new FieldName(name)); + } + + public Long getHttpStatusCode() { + return httpStatusCode; + } + + public void setHttpStatusCode(Long httpStatusCode) { + this.httpStatusCode = httpStatusCode; + } + + public Long getErrorCode() { + return errorCode; + } + + public void setErrorCode(Long errorCode) { + this.errorCode = errorCode; + } + + public List getFields() { + return fields; + } + + public void setFields(List fields) { + this.fields = fields; + } + + @Override + public String toString() { + return "FieldsValidator [httpStatusCode=" + httpStatusCode + ", errorCode=" + errorCode + ", fields=" + fields + + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((errorCode == null) ? 0 : errorCode.hashCode()); + result = prime * result + ((fields == null) ? 0 : fields.hashCode()); + result = prime * result + ((httpStatusCode == null) ? 0 : httpStatusCode.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + FieldsValidator other = (FieldsValidator) obj; + if (errorCode == null) { + if (other.errorCode != null) + return false; + } else if (!errorCode.equals(other.errorCode)) + return false; + if (fields == null) { + if (other.fields != null) + return false; + } else if (!fields.equals(other.fields)) + return false; + if (httpStatusCode == null) { + if (other.httpStatusCode != null) + return false; + } else if (!httpStatusCode.equals(other.httpStatusCode)) + return false; + return true; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuItem.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuItem.java new file mode 100644 index 00000000..707b08bc --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuItem.java @@ -0,0 +1,100 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.io.Serializable; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Transient; + +@Entity +@Table(name="fn_menu_functional") +public class FunctionalMenuItem implements Serializable { + public FunctionalMenuItem(){}; + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name = "MENU_ID") + public Long menuId; + + @Column(name = "COLUMN_NUM") + public Integer column; + + @Column(name = "TEXT") + public String text; + + @Column(name = "PARENT_MENU_ID") + public Integer parentMenuId; + + @Column(name = "URL") + public String url; + + @Column(name="ACTIVE_YN") + public String active_yn; + + @Transient + public Integer appid; + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Transient + private List roles; + + @Transient + public Boolean restrictedApp; + + public void normalize() { + if (this.column == null) + this.column = new Integer(1); + this.text = (this.text == null) ? "" : this.text.trim(); + if (this.parentMenuId == null) + this.parentMenuId = new Integer(-1); + this.url = (this.url == null) ? "" : this.url.trim(); + } + + @Override + public String toString() { + return "FunctionalMenuItem [menuId=" + menuId + ", column=" + column + ", text=" + text + ", parentMenuId=" + + parentMenuId + ", url=" + url + ", active_yn=" + active_yn + ", appid=" + appid + ", roles=" + roles + + ", restrictedApp=" + restrictedApp + "]"; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setRestrictedApp(Boolean restrictedApp) { + this.restrictedApp = restrictedApp; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuItemWithRoles.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuItemWithRoles.java new file mode 100644 index 00000000..8e33a5f7 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuItemWithRoles.java @@ -0,0 +1,60 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.io.Serializable; +import java.util.List; + +// This type is used to read the Json in from the API call from the Front End +public class FunctionalMenuItemWithRoles implements Serializable { + private static final long serialVersionUID = 1L; + + public Long menuId; + + public Integer column; + + public String text; + + public Integer parentMenuId; + + public String url; + + public Integer appid; + + private List roles; + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + public void normalize() { + if (this.column == null) + this.column = new Integer(1); + this.text = (this.text == null) ? "" : this.text.trim(); + if (this.parentMenuId == null) + this.parentMenuId = new Integer(-1); + this.url = (this.url == null) ? "" : this.url.trim(); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuRole.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuRole.java new file mode 100644 index 00000000..0033b62a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/FunctionalMenuRole.java @@ -0,0 +1,115 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="fn_menu_functional_roles") +public class FunctionalMenuRole implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name = "ID") + public Integer id; + + @Column(name = "MENU_ID") + public Long menuId; + + @Column(name = "APP_ID") + public Integer appId; + + @Column(name = "ROLE_ID") + public Integer roleId; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public Integer getAppId() { + return appId; + } + + public void setAppId(Integer appId) { + this.appId = appId; + } + + public Integer getRoleId() { + return roleId; + } + + public void setRoleId(Integer roleId) { + this.roleId = roleId; + } + + @Override + public String toString() { + return "FunctionalMenuRole [id=" + id + ", menuId=" + menuId + ", appId=" + appId + ", roleId=" + roleId + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((appId == null) ? 0 : appId.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((menuId == null) ? 0 : menuId.hashCode()); + result = prime * result + ((roleId == null) ? 0 : roleId.hashCode()); + return result; + } + + @Override + public boolean equals(Object o) { + + if (o == this) return true; + if (!(o instanceof FunctionalMenuRole)) { + return false; + } + FunctionalMenuRole functionalMenuRole = (FunctionalMenuRole) o; + System.out.println("test"); + return id.equals(functionalMenuRole.getId()) && + menuId.equals(functionalMenuRole.menuId) && + appId.equals(functionalMenuRole.appId) && + roleId.equals(functionalMenuRole.roleId) ; + } + + + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/LocalRole.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/LocalRole.java new file mode 100644 index 00000000..06fd6aec --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/LocalRole.java @@ -0,0 +1,42 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class LocalRole implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @Column(name = "ROLE_ID") + public Integer roleId; + + @Column(name = "ROLE_NAME") + public String rolename; + + public void normalize() { + this.rolename = (this.rolename == null) ? "" : this.rolename.trim(); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/OnboardingApp.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/OnboardingApp.java new file mode 100644 index 00000000..977269d2 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/OnboardingApp.java @@ -0,0 +1,99 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +/** + * Model of rows in the fn_app table; serialized as a message add or update an + * on-boarded application. + */ +public class OnboardingApp { + + public Long id; + + public String name; + + public String imageUrl; + + public String description; + + public String notes; + + public String url; + + public String alternateUrl; + + public String restUrl; + + public Boolean isOpen; + + public Boolean isEnabled; + + public Long motsId; + + public String myLoginsAppName; + + public String myLoginsAppOwner; + + public String username; + + public String appPassword; + + public String thumbnail; + + public String uebTopicName; + + public String uebKey; + + public String uebSecret; + + public Boolean restrictedApp; + + /** + * Sets the name, myLoginsAppName, myLoginsAppOwner, username and + * appPassword fields to the empty string OR trims leading/trailing space, + * as appropriate. + */ + public void normalize() { + this.name = (this.name == null) ? "" : this.name.trim(); + this.myLoginsAppName = (this.myLoginsAppName == null) ? "" : this.myLoginsAppName.trim(); + this.myLoginsAppOwner = (this.myLoginsAppOwner == null) ? "" : this.myLoginsAppOwner.trim(); + this.username = (this.username == null) ? "" : this.username.trim(); + this.appPassword = (this.appPassword == null) ? "" : this.appPassword.trim(); + } + + public void setUebTopicName(String topicName) { + this.uebTopicName = topicName; + } + + public void setUebKey(String key) { + this.uebKey = key; + } + + public void setUebSecret(String secret) { + this.uebSecret = secret; + } + + // Hide the implementation of restricted and normal app from the front end. + // The json sent and received will include restrictedApp but not appType. + + public void setRestrictedApp(Boolean restrictedApp) { + this.restrictedApp = restrictedApp; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/OnboardingWidget.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/OnboardingWidget.java new file mode 100644 index 00000000..09471d7f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/OnboardingWidget.java @@ -0,0 +1,65 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class OnboardingWidget implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @Column(name = "WIDGET_ID") + public Long id; + + @Column(name = "WDG_NAME") + public String name; + + @Column(name = "APP_ID") + public Long appId; + + @Column(name = "APP_NAME") + public String appName; + + @Column(name = "WDG_WIDTH") + public Integer width; + + @Column(name = "WDG_HEIGHT") + public Integer height; + + @Column(name = "WDG_URL") + public String url; + + public void normalize() { + this.name = (this.name == null) ? "" : this.name.trim(); + this.appName = (this.appName == null) ? "" : this.appName.trim(); + if (this.width == null) + this.width = new Integer(0); + if (this.height == null) + this.height = new Integer(0); + this.url = (this.url == null) ? "" : this.url.trim(); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/PortalAdmin.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/PortalAdmin.java new file mode 100644 index 00000000..0481eb61 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/PortalAdmin.java @@ -0,0 +1,53 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * This is to handle portal admins + */ +@Entity +@Table(name="fn_user") +public class PortalAdmin implements Serializable{ + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name = "user_id") + public Long userId; + + @Column(name = "login_id") + public String loginId; + + @Column(name = "first_name") + public String firstName; + + @Column(name = "last_name") + public String lastName; + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/PortalAdminUserRole.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/PortalAdminUserRole.java new file mode 100644 index 00000000..06229fc1 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/PortalAdminUserRole.java @@ -0,0 +1,48 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * This is to handle portal admins + */ +@Entity +@Table(name="fn_user_role") +public class PortalAdminUserRole implements Serializable{ + private static final long serialVersionUID = 1L; + + @Id + @Column(name = "user_id") + public Long userId; + + @Id + @Column(name = "role_id") + public Long roleId; + + @Column(name = "app_id") + public Long appId; + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RemoteRole.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RemoteRole.java new file mode 100644 index 00000000..0f152772 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RemoteRole.java @@ -0,0 +1,44 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +public class RemoteRole { + + private Long id; + private String name; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "RemoteRole [id=" + id + ", name=" + name + "]"; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RemoteUserWithRoles.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RemoteUserWithRoles.java new file mode 100644 index 00000000..7095991c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RemoteUserWithRoles.java @@ -0,0 +1,197 @@ +package org.openecomp.portalapp.portal.transport; + +import java.util.ArrayList; +import java.util.List; + +/*- + * ================================================================================ + * ECOMP Portal SDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +/** + * User description which we receive in response from request to remote + * application: applicationsRestClientService.get(RemoteUserWithRoles[].class, + * appId, "/users"). It contains the most important info about remote + * application user including his roles in this application. + */ +public class RemoteUserWithRoles { + + private Long orgId; + + private Long managerId; + + private String firstName; + + private String middleInitial; + + private String lastName; + + private String phone; + + private String email; + + private String hrid; + + private String orgUserId; + + private String orgCode; + + private String orgManagerUserId; + + private String jobTitle; + + private String loginId; + + private Boolean active; + + private List roles = new ArrayList(); + + public Long getOrgId() { + return orgId; + } + + public void setOrgId(Long orgId) { + this.orgId = orgId; + } + + public Long getManagerId() { + return managerId; + } + + public void setManagerId(Long managerId) { + this.managerId = managerId; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getMiddleInitial() { + return middleInitial; + } + + public void setMiddleInitial(String middleInitial) { + this.middleInitial = middleInitial; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getHrid() { + return hrid; + } + + public void setHrid(String hrid) { + this.hrid = hrid; + } + + public String getOrgUserId() { + return orgUserId; + } + + public void setOrgUserId(String orgUserId) { + if(this.orgUserId == null) + this.orgUserId = orgUserId; + } + + public String getOrgCode() { + return orgCode; + } + + public void setOrgCode(String orgCode) { + this.orgCode = orgCode; + } + + public String getOrgManagerUserId() { + return orgManagerUserId; + } + + public void setOrgManagerUserId(String orgManagerUserId) { + this.orgManagerUserId = orgManagerUserId; + } + + public String getJobTitle() { + return jobTitle; + } + + public void setJobTitle(String jobTitle) { + this.jobTitle = jobTitle; + } + + public String getLoginId() { + return loginId; + } + + public void setLoginId(String loginId) { + this.loginId = loginId; + } + + public Boolean getActive() { + return active; + } + + public void setActive(Boolean active) { + this.active = active; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Override + public String toString() { + return "RemoteUserWithRoles [orgId=" + orgId + ", managerId=" + managerId + ", firstName=" + firstName + + ", middleInitial=" + middleInitial + ", lastName=" + lastName + ", phone=" + phone + ", email=" + + email + ", hrid=" + hrid + ", orgUserId=" + orgUserId + ", orgCode=" + orgCode + ", orgManagerUserId=" + + orgManagerUserId + ", jobTitle=" + jobTitle + ", loginId=" + loginId + ", active=" + active + + ", roles=" + roles + "]"; + } + + + + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RoleInAppForUser.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RoleInAppForUser.java new file mode 100644 index 00000000..dad1c98b --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RoleInAppForUser.java @@ -0,0 +1,105 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +public class RoleInAppForUser { + + + public RoleInAppForUser() { + } + + public RoleInAppForUser(Long id, String name) { + this.roleId = id; + this.roleName = name; + } + + public Long roleId; + + public String roleName; + + public Boolean isApplied; + + public Long getRoleId() { + return roleId; + } + + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public Boolean getIsApplied() { + return isApplied; + } + + public void setIsApplied(Boolean isApplied) { + this.isApplied = isApplied; + } + + @Override + public String toString() { + return "RoleInAppForUser [roleId=" + roleId + ", roleName=" + roleName + ", isApplied=" + isApplied + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((isApplied == null) ? 0 : isApplied.hashCode()); + result = prime * result + ((roleId == null) ? 0 : roleId.hashCode()); + result = prime * result + ((roleName == null) ? 0 : roleName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RoleInAppForUser other = (RoleInAppForUser) obj; + if (isApplied == null) { + if (other.isApplied != null) + return false; + } else if (!isApplied.equals(other.isApplied)) + return false; + if (roleId == null) { + if (other.roleId != null) + return false; + } else if (!roleId.equals(other.roleId)) + return false; + if (roleName == null) { + if (other.roleName != null) + return false; + } else if (!roleName.equals(other.roleName)) + return false; + return true; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RolesInAppForUser.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RolesInAppForUser.java new file mode 100644 index 00000000..8720304d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/RolesInAppForUser.java @@ -0,0 +1,33 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.ArrayList; +import java.util.List; + +public class RolesInAppForUser { + + public String orgUserId; + + public Long appId; + + public List roles = new ArrayList(); + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/UserApplicationRoles.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/UserApplicationRoles.java new file mode 100644 index 00000000..75aba7ab --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/UserApplicationRoles.java @@ -0,0 +1,86 @@ +/*- + * ================================================================================ + * ECOMP Portal SDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +import java.util.ArrayList; +import java.util.List; + +/** + * A specific application user and his application specific roles. + * + */ +public class UserApplicationRoles { + + private Long appId; + + private String orgUserId; + + private String firstName; + + private String lastName; + + private List roles = new ArrayList(); + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public String getOrgUserId() { + return orgUserId; + } + + public void setOrgUserId(String orgUserId) { + this.orgUserId = orgUserId; + } + + 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 List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Override + public String toString() { + return "UserApplicationRoles [appId=" + appId + ", orgUserId=" + orgUserId + ", firstName=" + firstName + + ", lastName=" + lastName + ", roles=" + roles + "]"; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/UserWithNameSurnameTitle.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/UserWithNameSurnameTitle.java new file mode 100644 index 00000000..ba411e65 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/UserWithNameSurnameTitle.java @@ -0,0 +1,39 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +public class UserWithNameSurnameTitle { + + public String orgUserId; + + public String firstName; + + public String lastName; + + public String jobTitle; + + public UserWithNameSurnameTitle(String orgUserId, String firstName, String lastName, String jobTitle) { + this.orgUserId = orgUserId; + this.firstName = firstName; + this.lastName = lastName; + this.jobTitle = jobTitle; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/WidgetCatalogPersonalization.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/WidgetCatalogPersonalization.java new file mode 100644 index 00000000..5117e8f2 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/transport/WidgetCatalogPersonalization.java @@ -0,0 +1,57 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.transport; + +/** + * Model for the object PUT to the controller when the user takes an action on + * a widget in the catalog. + */ + +public class WidgetCatalogPersonalization { + + public Long widgetId; + public Boolean select; + // not needed as only 'SHOW' and 'HIDE' status_cd is expected from the micro service now + //public Boolean pending; + + public Long getWidgetId() { + return widgetId; + } + + public void setWidgetId(Long widgetId) { + this.widgetId = widgetId; + } + + public Boolean getSelect() { + return select; + } + + public void setSelect(Boolean select) { + this.select = select; + } + + /*public Boolean getPending() { + return pending; + } + + public void setPending(Boolean pending) { + this.pending = pending; + }*/ +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ueb/EPUebHelper.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ueb/EPUebHelper.java new file mode 100644 index 00000000..3c447e2a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ueb/EPUebHelper.java @@ -0,0 +1,217 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.ueb; + +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.LinkedList; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.util.PortalApiConstants; +import org.openecomp.portalsdk.core.onboarding.util.PortalApiProperties; +import org.openecomp.portalsdk.core.onboarding.ueb.Helper; +import org.openecomp.portalsdk.core.onboarding.ueb.Publisher; +import org.openecomp.portalsdk.core.onboarding.ueb.UebException; +import org.openecomp.portalsdk.core.onboarding.ueb.UebManager; +import org.openecomp.portalsdk.core.onboarding.ueb.UebMsg; +import org.openecomp.portalapp.portal.domain.EPApp; +import org.openecomp.portalapp.portal.domain.EcompApp; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.service.EPAppService; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; + +@Component +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +public class EPUebHelper { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPUebHelper.class); + + @Autowired + EPAppService appsService; + + + @Autowired + private SessionFactory sessionFactory; + + @SuppressWarnings("unused") + private Publisher epPublisher; + + public EPUebHelper() { + + } + // + // This should only be called by the ECOMP Portal App, other Apps have just one publisher and use appPublisher + // + @SuppressWarnings("unused") + @EPMetricsLog + public void refreshPublisherList() + { + Session localSession = null; + boolean addedPublisher = false; + + try { + localSession = sessionFactory.openSession(); + + List apps = appsService.getEcompAppAppsFullList(); + for (int i = 0; i < apps.size(); i++) + { + if ((apps.get(i).isEnabled()) && + (apps.get(i).getUebTopicName() != null) && + !(apps.get(i).getUebTopicName().toUpperCase().contains("ECOMP-PORTAL-INBOX"))) + { + logger.debug(EELFLoggerDelegate.debugLogger, "UEBManager adding publisher for " + apps.get(i).getUebTopicName()); + UebManager.getInstance().addPublisher(apps.get(i).getUebTopicName()); + addedPublisher = true; + } + else if ((apps.get(i).getId() != 1) && // App may have been disabled, remove the publisher + !(apps.get(i).isEnabled())) + { + if(apps.get(i).getUebTopicName()!=null){ + UebManager.getInstance().removePublisher(apps.get(i).getUebTopicName()); + } + } + } + } + catch (Exception e) + { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUebSystemError, e, "add/remove Publisher"); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while refreshing the publisher list", e); + } + + //publisherList.print(); + + if (addedPublisher == true) // Give publishers time to initialize + { + Helper.sleep(400); + } + } + + @PostConstruct + @EPMetricsLog + public void initUeb() { + try { + epPublisher = new Publisher(PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_KEY), + PortalApiProperties.getProperty(PortalApiConstants.UEB_APP_SECRET), + PortalApiProperties.getProperty(PortalApiConstants.ECOMP_PORTAL_INBOX_NAME)); + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUebConnectionError, e); + String stackTrace = EcompPortalUtils.getStackTrace(e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while initializing the publisher. Details: " + stackTrace); + } + + Thread thread = new Thread("EPUebManager: postConstructMethod - refreshPublisherList") { + public void run(){ + refreshPublisherList(); + } + }; + if (thread != null) { + thread.start(); + } + } + + @EPMetricsLog + public void addPublisher(EPApp app) { + // TODO Auto-generated method stub + try { + UebManager.getInstance().addPublisher(app.getUebTopicName()); + } catch (UebException e) { + String stackTrace = EcompPortalUtils.getStackTrace(e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception while adding a publisher. Details: " + stackTrace); + } + } + + public boolean checkAvailability() { + + // + // Test existence of topic at UEB url + // + // (ie http://uebsb91kcdc.it.att.com:3904/topics/ECOMP-PORTAL-INBOX) + // + boolean available = true; + LinkedList urlList = Helper.uebUrlList(); + if (!urlList.isEmpty()) { + String url = "http://" + urlList.getFirst() + ":3904/topics/" + PortalApiProperties.getProperty(PortalApiConstants.ECOMP_PORTAL_INBOX_NAME); + if (!url.isEmpty()) { + try { + URL siteURL = new URL(url); + HttpURLConnection connection = (HttpURLConnection) siteURL.openConnection(); + connection.setRequestMethod("GET"); + connection.connect(); + + int code = connection.getResponseCode(); + if (code == 200) { + available = true; + } + else { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeUebConnectionError, url); + available = false; + logger.warn(EELFLoggerDelegate.errorLogger, "Warning! UEB topic existence check failed, topic = " + url ); + logger.debug(EELFLoggerDelegate.debugLogger, "Warning! UEB topic existence check failed, topic = " + url ); + } + } + catch (Exception e) { + available = false; + String stackTrace = EcompPortalUtils.getStackTrace(e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while performing the UEB Healthcheck. Details: " + stackTrace); + } + } + } + return available; + } + + public boolean MessageCanBeSentToTopic() { + + boolean sentMsgSuccessfully = false; + + UebMsg msg = new UebMsg(); + msg.putSourceTopicName(PortalApiProperties.getProperty(PortalApiConstants.ECOMP_PORTAL_INBOX_NAME)); + msg.putPayload("Pinging topic for health check"); + msg.putMsgType(EPUebMsgTypes.UEB_MSG_TYPE_HEALTH_CHECK); + + try { + // epPublisher.send(msg); + sentMsgSuccessfully = true; + } + catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeHealthCheckUebClusterError, e); + sentMsgSuccessfully = false; + logger.warn(EELFLoggerDelegate.errorLogger, "Warning! could not successfully publish a UEB msg to " + + PortalApiProperties.getProperty(PortalApiConstants.ECOMP_PORTAL_INBOX_NAME), e); + } + + return sentMsgSuccessfully; + } + +} + + diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ueb/EPUebMsgTypes.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ueb/EPUebMsgTypes.java new file mode 100644 index 00000000..aef02545 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/ueb/EPUebMsgTypes.java @@ -0,0 +1,27 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.ueb; + +import org.openecomp.portalsdk.core.onboarding.ueb.UebMsgTypes; + +public interface EPUebMsgTypes extends UebMsgTypes { + + public static final String UEB_MSG_TYPE_HEALTH_CHECK = "uebHealthCheckPing"; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/CustomLoggingFilter.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/CustomLoggingFilter.java new file mode 100644 index 00000000..a43f5177 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/CustomLoggingFilter.java @@ -0,0 +1,54 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.utils; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; + +/** + * Custom Filter class bind with logback.xml + * configuration file to strip out certain log messages + * coming out of special packages or classes. + * + */ +public class CustomLoggingFilter extends Filter { + + /** + * Custom Filter is added to strip out the continuous U-EB logging messages + * But make sure we log the ERROR & WARNING Level messages. + */ + @Override + public FilterReply decide(ILoggingEvent event) { + try { + if ((event.getLevel() != Level.ERROR || event.getLevel() != Level.WARN) && + (event.getThreadName().equalsIgnoreCase("UEBConsumerThread")) && + (event.getLoggerName().contains("org.openecomp.nsa") || event.getLoggerName().contains("org.apache.http")) + ) { + return FilterReply.DENY; + } else { + return FilterReply.NEUTRAL; + } + } catch(Exception e) { + return FilterReply.NEUTRAL; + } + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EPCommonSystemProperties.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EPCommonSystemProperties.java new file mode 100644 index 00000000..38b6608a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EPCommonSystemProperties.java @@ -0,0 +1,64 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.utils; + +import org.openecomp.portalsdk.core.util.SystemProperties; + +public class EPCommonSystemProperties extends SystemProperties { + + public static final String LOGIN_URL_NO_RET_VAL = "login_url_no_ret_val"; + public static final String ECOMP_APP_ID = "ecomp_app_id"; + public static final String SYS_ADMIN_ROLE_ID = "sys_admin_role_id"; + public static final String DUBLICATED_FIELD_VALUE_ECOMP_ERROR = "1201"; + public static final String ACCOUNT_ADMIN_ROLE_ID = "account_admin_role_id"; + public static final String RESTRICTED_APP_ROLE_ID = "restricted_app_role_id"; + public static final String FE_URL = "frontend_url"; + public static final String HEALTH_POLL_INTERVAL_SECONDS = "health_poll_interval_seconds"; + public static final String HEALTHFAIL_ALERT_EVERY_X_INTERVALS = "health_fail_alert_every_x_intervals"; + public static final String USER_GUIDE_URL = "user_guide_link"; + + public static final String USER_FIRST_NAME = "USER_FIRST_NAME"; + public static final String USER_LAST_NAME = "USER_LAST_NAME"; + public static final String USER_EMAIL = "USER_EMAIL"; + public static final String USER_ORG_USERID = "USER_ORG_USERID"; + + public static final String USH_TICKET_URL = "ush_ticket_url"; + + public static final String EXTERNAL_API_RESPONSE_CODE = "External_API_ResponseCode"; + public static final String COOKIE_DOMAIN = "cookie_domain"; + + public static final String FEEDBACK_EMAIL_ADDRESS = "feedback_email_address"; + public static final String PORTAL_INFO_URL = "portal_info_url"; + + public static final String ONLINE_USER_UPDATE_RATE = "online_user_update_rate"; + public static final String ONLINE_USER_UPDATE_DURATION = "online_user_update_duration"; + + public static final String NOTIFICATION_UPDATE_RATE = "notification_update_rate"; + public static final String NOTIFICATION_UPDATE_DURATION = "notification_update_duration"; + + public static final String WINDOW_WIDTH_THRESHOLD_LEFT_MENU = "window_width_threshold_left_menu"; + public static final String WINDOW_WIDTH_THRESHOLD_RIGHT_MENU = "window_width_threshold_right_menu"; + + public static final String AUDITLOG_DEL_DAY_FROM = "auditlog_del_day_from"; + public static final String AUDITLOG_DELETE_CRON = "auditlog_delete_cron"; + + public static final String AUTH_USER_SERVER = "auth_user_server"; + public static final String EXTERNAL_ACCESS_ENABLE = "external_access_enable"; +} \ No newline at end of file diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EcompPortalUtils.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EcompPortalUtils.java new file mode 100644 index 00000000..adf36b2b --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EcompPortalUtils.java @@ -0,0 +1,331 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.slf4j.MDC; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class EcompPortalUtils { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EcompPortalUtils.class); + + /** + * @param orgUserId + * @return true if orgUserId is not empty and contains only alphanumeric, + * false otherwise + */ + public static boolean legitimateUserId(String orgUserId) { + return orgUserId.matches("^[a-zA-Z0-9]+$"); + } + + /** + * Splits the string into a list of tokens using the specified regular + * expression + * + * @param source + * @param regex + * @return List of tokens split from the source + */ + public static List parsingByRegularExpression(String source, String regex) { + List tokens = new ArrayList(); + if (source != null && source.length() > 0) { + String[] parsed = source.split(regex); + for (String token : parsed) { + if (token.length() > 0) { + tokens.add(token); + } + } + } + return tokens; + } + + /** + * Builds a JSON object with error code and message information. + * + * @param errorCode + * @param errorMessage + * @return JSON object as a String + */ + public static String jsonErrorMessageResponse(int errorCode, String errorMessage) { + return "{\"error\":{\"code\":" + errorCode + "," + "\"message\":\"" + errorMessage + "\"}}"; + } + + /** + * Builds a JSON object with the specified message + * + * @param message + * @return JSON object as a String + */ + public static String jsonMessageResponse(String message) { + return String.format("{\"message\":\"%s\"}", message); + } + + /** + * Serializes the specified object as JSON and writes the result to the + * debug log. If serialization fails, logs a message to the error logger. + * + * @param logger + * Logger for the class where the object was built; the logger + * carries the class name. + * @param source + * First portion of the log message + * @param msg + * Second portion of the log message + * @param obj + * Object to serialize as JSON + */ + public static void logAndSerializeObject(EELFLoggerDelegate logger, String source, String msg, Object obj) { + try { + String objectAsJson = new ObjectMapper().writeValueAsString(obj); + logger.debug(EELFLoggerDelegate.debugLogger, + String.format("source= [%s]; %s [%s];", source, msg, objectAsJson)); + } catch (JsonProcessingException e) { + logger.warn(EELFLoggerDelegate.errorLogger, "logAndSerializedObject failed to serialize", e); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "logAndSerializedObject failed", e); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e); + } + } + + /** + * Serializes the specified object as JSON and writes the result to the + * debug log. If serialization fails, logs a message to the error logger. + * + * @param source + * First portion of the log message + * @param msg + * Second portion of the log message + * @param obj + * Object to serialize as JSON + */ + public static void logAndSerializeObject(String source, String msg, Object obj) { + logAndSerializeObject(logger, source, msg, obj); + } + + public static void rollbackTransaction(Transaction transaction, String errorMessage) { + logger.error(EELFLoggerDelegate.errorLogger, errorMessage); + try { + if (transaction != null) { + transaction.rollback(); + } + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeExecuteRollbackError, e); + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while performing a rollback transaction", + e); + } + } + + public static void closeLocalSession(Session localSession, String errorMessage) { + logger.error(EELFLoggerDelegate.errorLogger, errorMessage); + try { + if (localSession != null) { + localSession.close(); + } + } catch (Exception e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeDaoCloseSessionError, e); + logger.error(EELFLoggerDelegate.errorLogger, errorMessage + ", closeLocalSession exception", e); + } + } + + // TODO: GLOBAL_LOGIN_URL is the same as in SessionTimeoutInterceptor. + // It should be defined in SystemProperties. + private static final String GLOBAL_LOGIN_URL = "global-login-url"; + + /** + * Set response status to Unauthorized if user == null and to Forbidden in + * all (!) other cases. Logging is not performed if invocator == null + * + * @param user + * @param response + * @param invocator + * - may be null + */ + public static void setBadPermissions(EPUser user, HttpServletResponse response, String invocator) { + if (user == null) { + String loginUrl = SystemProperties.getProperty(EPCommonSystemProperties.LOGIN_URL_NO_RET_VAL); + response.setHeader(GLOBAL_LOGIN_URL, loginUrl); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + MDC.put(EPCommonSystemProperties.RESPONSE_CODE, Integer.toString(HttpServletResponse.SC_UNAUTHORIZED)); + } else { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + MDC.put(EPCommonSystemProperties.RESPONSE_CODE, Integer.toString(HttpServletResponse.SC_FORBIDDEN)); + } + if (invocator != null) { + logger.warn(EELFLoggerDelegate.errorLogger, + invocator + ", permissions problem, response status = " + response.getStatus()); + } + } + + public static int getExternalAppResponseCode() { + String responseCode = MDC.get(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE); + int responseCodeInt = 0; + try { + if (responseCode != null && responseCode != "") { + responseCodeInt = Integer.valueOf(responseCode); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred in getResponseCode(). Details: " + EcompPortalUtils.getStackTrace(e)); + } + return responseCodeInt; + } + + // This method might be just for testing purposes. + public static void setExternalAppResponseCode(int responseCode) { + try { + String responseCodeString = String.valueOf(responseCode); + MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE, responseCodeString); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred in setResponseCode(). Details: " + EcompPortalUtils.getStackTrace(e)); + } + } + + public static String getHTTPStatusString(int httpStatusCode) { + String httpStatusString = "unknown_error"; + try { + httpStatusString = org.springframework.http.HttpStatus.valueOf(httpStatusCode).name(); + if (httpStatusString != null) { + httpStatusString = httpStatusString.toLowerCase(); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred in getHTTPStatusString(). Details: " + EcompPortalUtils.getStackTrace(e)); + } + return httpStatusString; + } + + public static String getFEErrorString(Boolean internal, int responseCode) { + // Return a String like the following: + // "Internal Ecomp Error: 500 internal_server_error" or + // "External App Error: 404 not_found" + // TODO: create our own Ecomp error codes, starting with 1000 and up. + String responseString = ""; + String internalExternalString = internal ? "Ecomp Error: " : "App Error: "; + String httpStatusString = "unknown_error"; + try { + if (responseCode < 1000) { + httpStatusString = getHTTPStatusString(responseCode); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, + "Exception occurred in getFEErrorString(). Details: " + EcompPortalUtils.getStackTrace(e)); + } + responseString = internalExternalString + responseCode + " " + httpStatusString; + return responseString; + } + + public static boolean isProductionBuild() { + boolean productionBuild = true; + String epVersion = EcompVersion.buildNumber; + if (epVersion != null) { + int buildNum = epVersion.lastIndexOf('.'); + if (buildNum > 0) { + int buildNumber = Integer.parseInt(epVersion.substring(buildNum + 1)); + if (buildNumber < 3000) // Production versions are 3000+, (ie + // 1.0.3003) + { + productionBuild = false; + } + } + } + return productionBuild; + } + + private static final Object stackTraceLock = new Object(); + + public static String getStackTrace(Throwable t) { + synchronized (stackTraceLock) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + t.printStackTrace(pw); + return sw.toString(); + } + } + + public static String getMyIpAdddress() { + InetAddress ip; + String localIp; + try { + ip = InetAddress.getLocalHost(); + localIp = ip.getHostAddress(); + } catch (UnknownHostException e) { + localIp = "unknown"; + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } + return localIp; + } + + public static String getMyHostName() { + InetAddress ip; + String hostName; + try { + ip = InetAddress.getLocalHost(); + hostName = ip.getHostName(); + } catch (UnknownHostException e) { + hostName = "unknown"; + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } + return hostName; + } + + /* return a default property if the expected one is not available */ + public static String getPropertyOrDefault(String property, String defaultValue) { + return ((null == SystemProperties.getProperty(property) || SystemProperties.getProperty(property).equals("")) + ? defaultValue : SystemProperties.getProperty(property)); + } + + public static void calculateDateTimeDifferenceForLog(String beginDateTime, String endDateTime) { + if (beginDateTime != null && endDateTime != null) { + try { + SimpleDateFormat ecompLogDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + + Date beginDate = ecompLogDateFormat.parse(beginDateTime); + Date endDate = ecompLogDateFormat.parse(endDateTime); + String timeDifference = String.format("%d", endDate.getTime() - beginDate.getTime()); + MDC.put(SystemProperties.MDC_TIMER, timeDifference); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } + } + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EcompVersion.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EcompVersion.java new file mode 100644 index 00000000..a4d5e0c1 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/EcompVersion.java @@ -0,0 +1,33 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.utils; + +// +// Hardcoded for now, wanted to use maven and resource file. Possibly just read +// pom.properties file with version added, but spent several hours already running +// into issues with that and other ideas not working. So, for 1604 deadlines I +// am just putting the version in the code here... for now. +// +public final class EcompVersion { + + // TODO - not used - manifest service reads from manifest + public static final String buildNumber="1610.1.999"; + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/HashMapFromList.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/HashMapFromList.java new file mode 100644 index 00000000..3a781af9 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/HashMapFromList.java @@ -0,0 +1,99 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; + +/** + * + * @author Vladimir Turovets This class is used to create HashMap + * from the list of T objects. We suppose that + * 1) T object contains field 'parmName' or getter 'getParmName()'. + * The value of object.parmName or object.getParmName() is used as + * a key in created hashMap. + * 2) for all objects in the list 'parmName' or getter + * 'getParmName().toString' has to be unique and not null. + * This class has one function only: + * HashMap hashMap(List list, String name) and returns + * hash map created from list. + * + * @param + */ +public class HashMapFromList { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(HashMapFromList.class); + + public HashMap hashMap(List list, String name) { + HashMap result = new HashMap(); + if (list == null || list.size() == 0 || name == null) { + return result; + } + name = name.trim(); + if (name.length() > 0) { + T object = list.get(0); + try { + String parmName = name; + Field field = object.getClass().getField(parmName); + for (T obj : list) { + try { + Object o = field.get(obj); + if (o != null) + result.put(o.toString(), obj); + } catch (Exception e1) { + String stackTrace = EcompPortalUtils.getStackTrace(e1); + logger.error(EELFLoggerDelegate.errorLogger, "Object of class " + object.getClass().getName() + ", field " + parmName + ". Details: ", stackTrace); + return new HashMap(); + } + } + } catch (Exception e) { + String getterName = "get" + (name.length() == 1 ? name.toUpperCase() : (name.substring(0, 1).toUpperCase() + name.substring(1))); + try { + Class[] parmClasses = null; + Method method = object.getClass().getMethod(getterName, parmClasses); + Object[] parmValues = new Object[0]; + for (T obj : list) { + try { + Object o = method.invoke(obj, parmValues); + if (o != null) + result.put(o.toString(), obj); + } catch (Exception e2) { + String stackTrace = EcompPortalUtils.getStackTrace(e2); + logger.error(EELFLoggerDelegate.errorLogger, "Object of class " + object.getClass().getName() + ", method " + getterName + ". Details: ", stackTrace); + return new HashMap(); + } + } + } catch (Exception e3) { + String stackTrace = EcompPortalUtils.getStackTrace(e3); + logger.error(EELFLoggerDelegate.errorLogger, "Object of class " + object.getClass().getName() + ", bad field '" + name + "' or method '" + getterName + "()'. Details: " + stackTrace); + return new HashMap(); + } + } + } + if (list.size() != result.size()) { + logger.warn(EELFLoggerDelegate.errorLogger, "Duplicated or empty keys were found!!!"); + } + return result; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/ParallelExecutor.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/ParallelExecutor.java new file mode 100644 index 00000000..1ddb550d --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/ParallelExecutor.java @@ -0,0 +1,83 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; + +public abstract class ParallelExecutor { + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ParallelExecutor.class); + + protected static abstract class ThreadOperation { + public abstract T execute(List parms); + } + + protected abstract ThreadOperation getThreadOperation(); + + private static class CallableOperationThread implements Callable { + + List parms; + + private ThreadOperation operation; + + public CallableOperationThread(ThreadOperation operation, List parms) { + this.parms = parms; + this.operation = operation; + } + + @Override + public T call() throws Exception { + return this.operation.execute(this.parms); + } + + } + + public List performAllOperations(int ThreadPoolSize, List> listOfParms) { + List result = new ArrayList(); + if (ThreadPoolSize > 0 && listOfParms != null) { + ExecutorService executor = Executors.newFixedThreadPool(ThreadPoolSize); + List> list = new ArrayList>(); + for (List parms : listOfParms) { + CallableOperationThread getter = new CallableOperationThread(this.getThreadOperation(), parms); + Future submit = executor.submit(getter); + list.add(submit); + } + for (Future future : list) { + try { + if (future != null) { + result.add(future.get()); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, EcompPortalUtils.getStackTrace(e)); + } + } + executor.shutdown(); + } + return result; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/PortalConstants.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/PortalConstants.java new file mode 100644 index 00000000..a0dc7d68 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/portal/utils/PortalConstants.java @@ -0,0 +1,26 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.portal.utils; + +public interface PortalConstants { + public static final Long PORTAL_APP_ID = 1L; + public static final Long DEFAULT_NOTIFICATION_CREATOR = 1L; + public static final String REST_AUX_API = "/auxapi"; +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/AdminAuthExtension.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/AdminAuthExtension.java new file mode 100644 index 00000000..3585f3a4 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/AdminAuthExtension.java @@ -0,0 +1,36 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalapp.portal.domain.EPUser; + + +@Service("adminAuthExtension") +@Transactional +public class AdminAuthExtension { + + public void saveUserExtension(EPUser user){ + //app's developer implement their own logic here, like updating app's related tables + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/EPProfileService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/EPProfileService.java new file mode 100644 index 00000000..0adde120 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/EPProfileService.java @@ -0,0 +1,36 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.service; + +import java.util.List; + +import org.openecomp.portalsdk.core.domain.Profile; +import org.openecomp.portalapp.portal.domain.EPUser; + + +public interface EPProfileService { + List findAll(); + + Profile getProfile(int id); + + EPUser getUser(String id); + + void saveUser(EPUser user); +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/EPProfileServiceImpl.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/EPProfileServiceImpl.java new file mode 100644 index 00000000..27975971 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/EPProfileServiceImpl.java @@ -0,0 +1,77 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.service; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import org.openecomp.portalsdk.core.dao.ProfileDao; +import org.openecomp.portalsdk.core.domain.Profile; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; + +@Service("epProfileService") +@Transactional +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class EPProfileServiceImpl implements EPProfileService { + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPProfileServiceImpl.class); + + @Autowired + private ProfileDao profileDao; + + @Autowired + private DataAccessService dataAccessService; + + @SuppressWarnings("unchecked") + public List findAll() { + // List msgDB = getDataAccessService().getList(Profile.class, null); + return getDataAccessService().getList(Profile.class, null); + } + + public EPUser getUser(String userId) { + return (EPUser) getDataAccessService().getDomainObject(EPUser.class, Long.parseLong(userId), null); + } + + public void saveUser(EPUser user) { + + getDataAccessService().saveDomainObject(user, null); + } + + public Profile getProfile(int id) { + return profileDao.getProfile(id); + } + + public DataAccessService getDataAccessService() { + return dataAccessService; + } + + public void setDataAccessService(DataAccessService dataAccessService) { + this.dataAccessService = dataAccessService; + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/RemoteWebServiceCallService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/RemoteWebServiceCallService.java new file mode 100644 index 00000000..71d9dd12 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/RemoteWebServiceCallService.java @@ -0,0 +1,48 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.service; + +public interface RemoteWebServiceCallService { + + /** + * Answers whether the specified credentials match application information + * in the database. + * + * @param secretKey + * Key used to decrypt passwords; ignored if null. + * @param requestUebKey + * UEB key that identifies the application + * @param requesUserName + * User name for the application + * @param requestPassword + * Password for the application + * @return True if the UEB key and the credentials match the database + * entries; else false. + * @throws Exception + * If decryption fails. + */ + public boolean verifyRESTCredential(String secretKey, String requestUebKey, String requestUserName, + String requestPassword) throws Exception; + + + + public boolean verifyAppKeyCredential(String requestUebKey) throws Exception; + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/CoreTimeoutHandler.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/CoreTimeoutHandler.java new file mode 100644 index 00000000..897d9cd9 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/CoreTimeoutHandler.java @@ -0,0 +1,166 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.service.sessionmgt; + +import java.util.Calendar; +import java.util.Hashtable; +import java.util.Map; + +import javax.servlet.http.HttpSession; + +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalsdk.core.domain.sessionmgt.TimeoutVO; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.util.PortalApiConstants; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + + +@Service +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class CoreTimeoutHandler{ + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CoreTimeoutHandler.class); + + public static final Map sessionMap = new Hashtable(); + public static final Integer repeatInterval = 15 * 60; // 15 minutes + ObjectMapper mapper = new ObjectMapper(); + + + public static void sessionCreated(String portalJSessionId, String jSessionId, HttpSession session) { + + storeMaxInactiveTime(session); + + // this key is a combination of portal jsession id and app session id + session.setAttribute(PortalApiConstants.PORTAL_JSESSION_ID,jSessionKey(jSessionId, portalJSessionId)); + sessionMap.put((String)session.getAttribute(PortalApiConstants.PORTAL_JSESSION_ID), session); + + } + + protected static void storeMaxInactiveTime(HttpSession session) { + + if(session.getAttribute(PortalApiConstants.GLOBAL_SESSION_MAX_IDLE_TIME) == null) + session.setAttribute(PortalApiConstants.GLOBAL_SESSION_MAX_IDLE_TIME,session.getMaxInactiveInterval()); + } + + public static void sessionDestroyed(HttpSession session) { + + try{ + sessionMap.remove((String)session.getAttribute(PortalApiConstants.PORTAL_JSESSION_ID)); + }catch(Exception e){ + logger.error(EELFLoggerDelegate.errorLogger, "************************ Session Management: Error while destroying session for " + session.getId() + " Details: " + EcompPortalUtils.getStackTrace(e)); + } + + } + + public String gatherSessionExtenstions() { + + Map sessionTimeoutMap = new Hashtable(); + String jsonMap = ""; + + + for(String jSessionKey: sessionMap.keySet()) { + + try{ + // get the expirytime in seconds + HttpSession session = sessionMap.get(jSessionKey); + + Long lastAccessedTimeMilliSec = session.getLastAccessedTime(); + Long maxIntervalMilliSec = session.getMaxInactiveInterval() * 1000L; + //Long currentTimeMilliSec = Calendar.getInstance().getTimeInMillis() ; + //(maxIntervalMilliSec - (currentTimeMilliSec - lastAccessedTimeMilliSec) + ; + Calendar instance = Calendar.getInstance(); + instance.setTimeInMillis(session.getLastAccessedTime()); + logger.info(EELFLoggerDelegate.errorLogger, "************************ Session Management: Last Accessed time for "+ jSessionKey + ": " + instance.getTime()); + + Long sessionTimOutMilliSec = maxIntervalMilliSec + lastAccessedTimeMilliSec; + + sessionTimeoutMap.put( portalJSessionId(jSessionKey), new TimeoutVO(jSessionId(jSessionKey),sessionTimOutMilliSec)); + + + jsonMap = mapper.writeValueAsString(sessionTimeoutMap); + } catch(Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "************************ Session Management: Error during JsonSessionTimout conversion. Details: " + EcompPortalUtils.getStackTrace(e)); + } + } + + return jsonMap; + + } + + + public void updateSessionExtensions(String sessionTimeoutMapStr) throws Exception { + + //Map sessionTimeoutMap = mapper.readValue(sessionTimeoutMapStr, Map.class); + Map sessionTimeoutMap; + try{ + TypeReference> typeRef + = new TypeReference>() {}; + + sessionTimeoutMap = mapper.readValue(sessionTimeoutMapStr, typeRef); + }catch(Exception e){ + logger.error(EELFLoggerDelegate.errorLogger, "************************ Session Management: Error while to parse update session extension in portal", e); + return; + } + for(String jPortalSessionId: sessionTimeoutMap.keySet()) { + try { + + TimeoutVO extendedTimeoutVO = mapper.readValue(mapper.writeValueAsString(sessionTimeoutMap.get(jPortalSessionId)),TimeoutVO.class); + HttpSession session = sessionMap.get(jSessionKey(extendedTimeoutVO.getjSessionId(), jPortalSessionId)); + + if(session == null) { + continue; + } + + Long lastAccessedTimeMilliSec = session.getLastAccessedTime(); + Long maxIntervalMilliSec = session.getMaxInactiveInterval() * 1000L; + Long sessionTimOutMilliSec = maxIntervalMilliSec + lastAccessedTimeMilliSec; + + Long maxTimeoutTimeMilliSec = extendedTimeoutVO.getSessionTimOutMilliSec(); + if(maxTimeoutTimeMilliSec>sessionTimOutMilliSec) { + logger.debug(EELFLoggerDelegate.debugLogger, "************************ Session Management: " + " updated session max idle time"); + session.setMaxInactiveInterval((int)(maxTimeoutTimeMilliSec-lastAccessedTimeMilliSec)/1000); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "************************ Session Management: " + EcompPortalUtils.getStackTrace(e)); + } + + } + + } + + protected static String jSessionKey(String jSessionId, String portalJSessionId) { + return portalJSessionId + "-" + jSessionId; + } + + protected String portalJSessionId(String jSessionKey) { + return jSessionKey.split("-")[0]; + } + + protected String jSessionId(String jSessionKey) { + return jSessionKey.split("-")[1]; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/ManageService.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/ManageService.java new file mode 100644 index 00000000..52cc0825 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/ManageService.java @@ -0,0 +1,101 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.service.sessionmgt; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.service.EPAppService; +import org.openecomp.portalapp.portal.transport.OnboardingApp; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.listener.PortalTimeoutHandler; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.quartz.CronExpression; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +@Service("manageService") +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class ManageService implements PortalTimeoutHandler.SessionCommInf { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ManageService.class); + + @Autowired + EPAppService appService; + + @Autowired + SessionCommunication sessionCommunication; + + public Integer fetchSessionSlotCheckInterval(String... params) throws Exception { + + String defaultCronExpressionStr = "0 0/5 * * * ? *"; + String cronExpressionStr = SystemProperties.getProperty(SystemProperties.SESSIONTIMEOUT_FEED_CRON); + + if (cronExpressionStr == null) { + cronExpressionStr = defaultCronExpressionStr; + } + + CronExpression cal = new CronExpression(cronExpressionStr); + final Date nowTime = Calendar.getInstance().getTime(); + Date nextTime = cal.getNextValidTimeAfter(nowTime); + Date nextNextTime = cal.getNextValidTimeAfter(nextTime); + + final int timeDiff = (int)(nextNextTime.getTime()-nextTime.getTime()); + logger.debug(EELFLoggerDelegate.debugLogger, "Time interval between subsequent session checks " + timeDiff); + + return timeDiff; + } + + public void extendSessionTimeOuts(String... params) throws Exception { + try { + String sessionMap = params[3]; + + logger.debug(EELFLoggerDelegate.debugLogger, "Extending the App sessions for last minute request: " + sessionMap); + + if (StringUtils.isEmpty(sessionMap)) { + logger.error(EELFLoggerDelegate.errorLogger, "extendSessionTimeOuts: Skipping session updates since the portal session value is empty."); + } else { + List appList = appService.getEnabledNonOpenOnboardingApps(); + for (OnboardingApp onApp : appList) { + sessionCommunication.pingSession(onApp, sessionMap); + } + updateSessionExtensions(sessionMap); + sessionCommunication.clear(false); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred in extendSessionTimeOuts(). Details: " + EcompPortalUtils.getStackTrace(e)); + } + } + + public String gatherSessionExtenstions() { + return PortalTimeoutHandler.gatherSessionExtensions(); + } + + public void updateSessionExtensions(String sessionTimeoutMapStr) throws Exception { + PortalTimeoutHandler.updateSessionExtensions(sessionTimeoutMapStr); + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/SessionCommunication.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/SessionCommunication.java new file mode 100644 index 00000000..11c0890e --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/SessionCommunication.java @@ -0,0 +1,271 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.service.sessionmgt; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.UUID; + +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.MDC; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import com.att.eelf.configuration.Configuration; +import org.openecomp.portalsdk.core.exception.UrlAccessRestrictedException; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalapp.portal.logging.aop.EPAuditLog; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.transport.OnboardingApp; +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; + +@Service("sessionCommunication") +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +public class SessionCommunication { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SessionCommunication.class); + + @EPAuditLog + public String sendGet(OnboardingApp app) throws Exception { + String appResponse = ""; + String appName = "Unknwon"; + int responseCode = 0; + + try { + if (app != null && app.name != null && app.name != "") { + appName = app.name; + } + String url = app.restUrl + "/sessionTimeOuts"; + String encriptedPwdDB = app.appPassword; + String appUserName = app.username; + + setLocalMDCContext(app, "/sessionTimeOuts", url); + + URL obj = new URL(url); + + HttpURLConnection con = (HttpURLConnection) obj.openConnection(); + + // optional default is GET + con.setRequestMethod("GET"); + con.setConnectTimeout(3000); + con.setReadTimeout(8000); + // add request header + con.setRequestProperty("username", appUserName); + con.setRequestProperty("password", encriptedPwdDB); + + // con.set + responseCode = con.getResponseCode(); + logger.debug(EELFLoggerDelegate.debugLogger, "Response Code : " + responseCode); + + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + + in.close(); + appResponse = response.toString(); + } catch (UrlAccessRestrictedException e) { + responseCode = HttpServletResponse.SC_UNAUTHORIZED; + logger.error(EELFLoggerDelegate.errorLogger, String.format("SessionCommunication.sendGet received an un-authorized exception. AppName: %s", appName)); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiAuthenticationError, e); + } catch (Exception e) { + responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; + String message = String.format( + "SessionCommunication.sendGet encountered an Exception. AppName: %s, Details: %s", appName, + EcompPortalUtils.getStackTrace(e)); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeHttpConnectionError, e); + logger.error(EELFLoggerDelegate.errorLogger, message); + } finally { + EcompPortalUtils.setExternalAppResponseCode(responseCode); + } + return appResponse; + } + + @EPAuditLog + public Boolean pingSession(OnboardingApp app, String sessionTimeoutMap) throws Exception { + String appName = "Unknwon"; + int responseCode = 0; + try { + if (app != null && app.name != null && app.name != "") { + appName = app.name; + } + + String url = app.restUrl + "/updateSessionTimeOuts"; + String encriptedPwdDB = app.appPassword; + String appUserName = app.username; + // String decreptedPwd = CipherUtil.decrypt(encriptedPwdDB, + // SystemProperties.getProperty(SystemProperties.Decryption_Key)); + + setLocalMDCContext(app, "/updateSessionTimeOuts", url); + + URL obj = new URL(url); + + HttpURLConnection con = (HttpURLConnection) obj.openConnection(); + + // optional default is GET + con.setRequestMethod("POST"); + con.setConnectTimeout(3000); + con.setReadTimeout(15000); + + // add request header + con.setRequestProperty("username", appUserName); + con.setRequestProperty("password", encriptedPwdDB); + + con.setRequestProperty("sessionMap", sessionTimeoutMap); + con.setDoInput(true); + con.setDoOutput(true); + con.getOutputStream().write(sessionTimeoutMap.getBytes()); + con.getOutputStream().flush(); + con.getOutputStream().close(); + + responseCode = con.getResponseCode(); + logger.debug(EELFLoggerDelegate.debugLogger, "Response Code : " + responseCode); + } catch (UrlAccessRestrictedException e) { + responseCode = HttpServletResponse.SC_UNAUTHORIZED; + String message = String.format( + "SessionCommunication.pingSession received an un-authorized exception. AppName: %s", appName); + logger.error(EELFLoggerDelegate.errorLogger, message); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiAuthenticationError, e); + } catch (Exception e) { + responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; + String message = String.format( + "SessionCommunication.pingSession encountered an Exception. AppName: %s, Details: %s", appName, + EcompPortalUtils.getStackTrace(e)); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeHttpConnectionError, e); + logger.error(EELFLoggerDelegate.errorLogger, message); + } finally { + EcompPortalUtils.setExternalAppResponseCode(responseCode); + } + + return true; + } + + @EPAuditLog + public Boolean timeoutSession(OnboardingApp app, String portalJSessionId) throws Exception { + String appName = "Unknwon"; + int responseCode = 0; + try { + if (app != null && app.name != null && app.name != "") { + appName = app.name; + } + + String url = app.restUrl + "/timeoutSession" + "?portalJSessionId=" + portalJSessionId; + + String encriptedPwdDB = app.appPassword; + String appUserName = app.username; + // String decreptedPwd = CipherUtil.decrypt(encriptedPwdDB, + // SystemProperties.getProperty(SystemProperties.Decryption_Key)); + + setLocalMDCContext(app, "/timeoutSession", url); + + URL obj = new URL(url); + HttpURLConnection con = (HttpURLConnection) obj.openConnection(); + + // optional default is GET + con.setRequestMethod("POST"); + con.setConnectTimeout(3000); + con.setReadTimeout(15000); + + // add request header + con.setRequestProperty("username", appUserName); + con.setRequestProperty("password", encriptedPwdDB); + + // con.setRequestProperty("portalJSessionId", portalJSessionId); + con.setDoInput(true); + con.setDoOutput(true); + con.getOutputStream().flush(); + con.getOutputStream().close(); + + responseCode = con.getResponseCode(); + logger.debug(EELFLoggerDelegate.debugLogger, "Response Code : " + responseCode); + } catch (UrlAccessRestrictedException e) { + responseCode = HttpServletResponse.SC_UNAUTHORIZED; + String message = String.format( + "SessionCommunication.timeoutSession received an un-authorized exception. AppName: %s", appName); + logger.error(EELFLoggerDelegate.errorLogger, message); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiAuthenticationError, e); + } catch (Exception e) { + responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; + String message = String.format( + "SessionCommunication.timeoutSession encountered an Exception. AppName: %s, Details: %s", appName, + EcompPortalUtils.getStackTrace(e)); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeHttpConnectionError, e); + logger.error(EELFLoggerDelegate.errorLogger, message); + } finally { + EcompPortalUtils.setExternalAppResponseCode(responseCode); + } + return true; + } + + @EPMetricsLog + private void setLocalMDCContext(OnboardingApp app, String restPath, String url) { + setRequestId(); + MDC.put(EPCommonSystemProperties.PROTOCOL, EPCommonSystemProperties.HTTP); + if (url!=null && url.contains("https")) { + MDC.put(EPCommonSystemProperties.PROTOCOL, EPCommonSystemProperties.HTTPS); + } + MDC.put(EPCommonSystemProperties.FULL_URL, url); + MDC.put(EPCommonSystemProperties.TARGET_ENTITY, app.myLoginsAppName); + MDC.put(EPCommonSystemProperties.TARGET_SERVICE_NAME, restPath); + } + + /** + * Generates request id, service name fields and loads them + * into MDC, as these values could be empty as these + * session timeout requests are generated at + * scheduled intervals using quartz scheduler. + */ + @EPMetricsLog + public void setRequestId() { + String requestId = MDC.get(Configuration.MDC_KEY_REQUEST_ID); + if (StringUtils.isEmpty(requestId)) { + MDC.put(Configuration.MDC_KEY_REQUEST_ID, UUID.randomUUID().toString()); + } + + MDC.put(Configuration.MDC_SERVICE_NAME, "/quartz/keepSessionAlive"); + MDC.put(EPCommonSystemProperties.PARTNER_NAME, EPCommonSystemProperties.ECOMP_PORTAL_BE); + } + + /** + * Remove the values from MDC as these requests are + * executed at regular intervals based on quartz rather + * incoming REST API requests. + * @param bAll + */ + @EPMetricsLog + public void clear(Boolean bAll) { + MDC.remove(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE); + if (bAll) { + MDC.remove(Configuration.MDC_KEY_REQUEST_ID); + MDC.remove(Configuration.MDC_SERVICE_NAME); + MDC.remove(EPCommonSystemProperties.PARTNER_NAME); + } + } +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/TimeoutHandler.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/TimeoutHandler.java new file mode 100644 index 00000000..53258c06 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/service/sessionmgt/TimeoutHandler.java @@ -0,0 +1,252 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.service.sessionmgt; + +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpSession; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.PersistJobDataAfterExecution; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import org.openecomp.portalsdk.core.domain.sessionmgt.TimeoutVO; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.util.PortalApiConstants; +import org.openecomp.portalapp.portal.logging.aop.EPMetricsLog; +import org.openecomp.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.openecomp.portalapp.portal.logging.logic.EPLogUtil; +import org.openecomp.portalapp.portal.service.EPAppService; +import org.openecomp.portalapp.portal.transport.OnboardingApp; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Executed periodically by Quartz to discover remote application sessions and + * update timeouts suitably. + */ +@PersistJobDataAfterExecution +@DisallowConcurrentExecution +@org.springframework.context.annotation.Configuration +@EnableAspectJAutoProxy +@EPMetricsLog +public class TimeoutHandler extends QuartzJobBean { + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(TimeoutHandler.class); + + ObjectMapper mapper = new ObjectMapper(); + + /** + * Supports static call {@link #timeoutSessions(HttpSession)} + */ + private static List onboardedAppList = null; + + @Autowired + SessionCommunication sessionCommunication; + + @Override + protected void executeInternal(JobExecutionContext context) throws JobExecutionException { + try { + //Create a request id if there is none available, + //and which will internally be used when making + //session extended timeout calls to the partner applications. + if (getSessionCommunication()!=null) { + getSessionCommunication().setRequestId(); + } + logger.info(EELFLoggerDelegate.debugLogger, "Quartz Cronjob for Session Management begins"); + + ManageService manageService = (ManageService) applicationContext.getBean("manageService"); + EPAppService appService = (EPAppService) applicationContext.getBean("epAppService"); + + List appList = appService.getEnabledNonOpenOnboardingApps(); + onboardedAppList = appList; + TypeReference> typeRef = new TypeReference>() { + }; + String portalJsonSessionStr; + Map portalSessionTimeoutMap = null; + + portalJsonSessionStr = manageService.gatherSessionExtenstions(); + if (portalJsonSessionStr == null || portalJsonSessionStr == "") { + logger.error(EELFLoggerDelegate.errorLogger, "Session Management: Portal session information is empty."); + return; + } + + try { + portalSessionTimeoutMap = mapper.readValue(portalJsonSessionStr, typeRef); + } catch (JsonMappingException | JsonParseException je) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, je); + logger.error(EELFLoggerDelegate.errorLogger, "Session Management: JSON Mapping Exception occurred while gathering the Session", je); + return; + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Session Management: Error while gather Session from portal", e); + return; + } + + Map> appSessionTimeOutMap = new Hashtable>(); + // determine the Max TimeOut Time for each of the managed sessions + for (OnboardingApp app : appList) { + if (app.restUrl == null) { + logger.info(EELFLoggerDelegate.debugLogger, "Session Management: null restUrl, not fetching from app " + app.name); + continue; + } + logger.info(EELFLoggerDelegate.debugLogger, "Session Management: Calling App " + app.name + " at URL " + app.restUrl); + String jsonSessionStr = fetchAppSessions(app); + logger.info(EELFLoggerDelegate.debugLogger, "Session Management: App " + app.name + " returned " + jsonSessionStr); + if (jsonSessionStr == null || jsonSessionStr.isEmpty()) + continue; + + try { + Map sessionTimeoutMap = mapper.readValue(jsonSessionStr, typeRef); + appSessionTimeOutMap.put(app.id, sessionTimeoutMap); + for (String portalJSessionId : sessionTimeoutMap.keySet()) { + final TimeoutVO maxTimeoutVO = portalSessionTimeoutMap.get(portalJSessionId); + final TimeoutVO compareTimeoutVO = sessionTimeoutMap.get(portalJSessionId); + if (maxTimeoutVO != null && compareTimeoutVO != null) { + if (maxTimeoutVO.compareTo(compareTimeoutVO) < 0) + portalSessionTimeoutMap.get(portalJSessionId) + .setSessionTimOutMilliSec(compareTimeoutVO.getSessionTimOutMilliSec()); + } + } + } catch (JsonParseException | JsonMappingException e) { + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e); + logger.error(EELFLoggerDelegate.errorLogger, + "JSON Mapping/Processing Exception occurred while mapping/parsing the jsonSessionStr", e); + continue; + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while mapping/parsing the jsonSessionStr", e); + continue; + } + + } + + // post the updated session timeouts back to the Apps + for (OnboardingApp app : appList) { + if (app.restUrl == null) { + logger.warn(EELFLoggerDelegate.errorLogger, "Session Management: null restUrl, not posting back to app " + app.name); + continue; + } + + Map sessionTimeoutMap = appSessionTimeOutMap.get(app.id); + if (sessionTimeoutMap == null || sessionTimeoutMap.isEmpty()) + continue; + + for (String portalJSessionId : sessionTimeoutMap.keySet()) { + try { + final TimeoutVO maxTimeoutVO = portalSessionTimeoutMap.get(portalJSessionId); + final TimeoutVO setTimeoutVO = sessionTimeoutMap.get(portalJSessionId); + if (maxTimeoutVO == null || setTimeoutVO == null) { + String message = String.format( + "Session Management: Failed to update the session timeouts for the app: %s and the sessionId: %s.", + app.name, portalJSessionId); + logger.warn(EELFLoggerDelegate.errorLogger, message); + continue; + } + setTimeoutVO.setSessionTimOutMilliSec(maxTimeoutVO.getSessionTimOutMilliSec()); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Session Management: error while updating the session timeout map", e); + continue; + } + } + logger.info(EELFLoggerDelegate.debugLogger, "Session Management: Updating App " + app.restUrl); + String sessionTimeoutMapStr = ""; + try { + sessionTimeoutMapStr = mapper.writeValueAsString(sessionTimeoutMap); + } catch (JsonProcessingException je) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while processing sessionTimeOutMap object to a String. Details: " + + EcompPortalUtils.getStackTrace(je)); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, je); + } + pingAppSessions(app, sessionTimeoutMapStr); + } + String portalSessionTimeoutMapStr = ""; + try { + portalSessionTimeoutMapStr = mapper.writeValueAsString(portalSessionTimeoutMap); + } catch (JsonProcessingException je) { + logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while processing portalSessionTimeOutMap object to a String", je); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, je); + } + manageService.updateSessionExtensions(portalSessionTimeoutMapStr); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "************************ Session Management: error in managing session timeouts", e); + } finally { + getSessionCommunication().clear(true); + } + } + + private String fetchAppSessions(OnboardingApp app) throws Exception { + String jsonSessionValue = getSessionCommunication().sendGet(app); + getSessionCommunication().clear(false); + return jsonSessionValue; + } + + private void pingAppSessions(OnboardingApp app, String sessionTimeoutMapStr) throws Exception { + getSessionCommunication().pingSession(app, sessionTimeoutMapStr); + getSessionCommunication().clear(false); + } + + public void timeoutSessions(HttpSession session) throws Exception { + String portalJSessionId = portalJSessionId(session); + if (onboardedAppList == null) + return; + + for (OnboardingApp app : onboardedAppList) { + getSessionCommunication().timeoutSession(app, portalJSessionId); + getSessionCommunication().clear(false); + } + } + + protected static String portalJSessionId(HttpSession session) { + final Object attribute = session.getAttribute(PortalApiConstants.PORTAL_JSESSION_ID); + if (attribute == null) + return ""; + String jSessionKey = (String) attribute; + return jSessionKey.split("-")[0]; + } + + private static ApplicationContext applicationContext; + + public static void setApplicationContext(ApplicationContext _applicationContext) { + applicationContext = _applicationContext; + } + + public SessionCommunication getSessionCommunication() { + if(sessionCommunication == null){ + if (applicationContext != null) + sessionCommunication = (SessionCommunication)applicationContext.getBean("sessionCommunication"); + } + + return sessionCommunication; + } + + public void setSessionCommunication(SessionCommunication sessionCommunication) { + this.sessionCommunication = sessionCommunication; + } + +} \ No newline at end of file diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/EPUserUtils.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/EPUserUtils.java new file mode 100644 index 00000000..a14bbe81 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/EPUserUtils.java @@ -0,0 +1,407 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.util; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.openecomp.portalapp.portal.domain.EPRole; +import org.openecomp.portalapp.portal.domain.EPUser; +import org.openecomp.portalapp.portal.domain.EPUserApp; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalsdk.core.domain.RoleFunction; +import org.openecomp.portalsdk.core.exception.SessionExpiredException; +import org.openecomp.portalsdk.core.lm.FusionLicenseManager; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.menu.MenuBuilder; +import org.openecomp.portalsdk.core.service.DataAccessService; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalsdk.core.web.support.AppUtils; +import org.springframework.beans.factory.annotation.Autowired; + +public class EPUserUtils { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(EPUserUtils.class); + + private final static Long ACCOUNT_ADMIN_ROLE_ID = 999L; + + public static final String ALL_ROLE_FUNCTIONS = "allRoleFunctions"; + + private static DataAccessService dataAccessService; + + /** + * Gets the EPUser object from the session. + * + * @param request + * HttpServletRequest + * @return EPUser object that was created upon login + * @throws SessionExpiredException + * if no session exists. + */ + public static EPUser getUserSession(HttpServletRequest request) { + HttpSession session = AppUtils.getSession(request); + if (session == null) + throw new SessionExpiredException(); + return (EPUser) session.getAttribute(SystemProperties.getProperty(SystemProperties.USER_ATTRIBUTE_NAME)); + } + + /** + * Establishes the user's portal session + * + * @param request + * HttpServletRequest + * @param user + * EPUser + * @param applicationMenuData + * Menu data + * @param businessDirectMenuData + * Menu data + * @param loginMethod_ignored + * How the user authenticated; ignored + * @param allRoleFunctions + * Set of user's roles + */ + @SuppressWarnings("rawtypes") + public static void setUserSession(HttpServletRequest request, EPUser user, Set applicationMenuData, + Set businessDirectMenuData, String loginMethod_ignored, List allRoleFunctions) { + HttpSession session = request.getSession(true); + + // clear the current user session to avoid any conflicts + EPUserUtils.clearUserSession(request); + session.setAttribute(SystemProperties.getProperty(SystemProperties.USER_ATTRIBUTE_NAME), user); + + getAllRoleFunctions(allRoleFunctions, session); + + getRoleFunctions(request); + + // truncate the role (and therefore the role function) data to save + // memory in the session + user.setEPRoles(null); + session.setAttribute(SystemProperties.getProperty(SystemProperties.USER_NAME), user.getFullName()); + + ServletContext context = session.getServletContext(); + int licenseVerificationFlag = 3; + try { + licenseVerificationFlag = (Integer) context.getAttribute("licenseVerification"); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "setUserSession failed to get licenseVerification attribute", + e); + } + switch (licenseVerificationFlag) { + case FusionLicenseManager.DEVELOPER_LICENSE: + session.setAttribute(SystemProperties.getProperty(SystemProperties.APP_DISPLAY_NAME), + "My Portal [Development Version]"); + break; + case FusionLicenseManager.EXPIRED_LICENSE: + session.setAttribute(SystemProperties.getProperty(SystemProperties.APP_DISPLAY_NAME), + "My Portal [LICENSE EXPIRED]"); + break; + case FusionLicenseManager.VALID_LICENSE: + session.setAttribute(SystemProperties.getProperty(SystemProperties.APP_DISPLAY_NAME), "My Portal"); + break; + default: + session.setAttribute(SystemProperties.getProperty(SystemProperties.APP_DISPLAY_NAME), + "My Portal [INVALID LICENSE]"); + break; + } + + session.setAttribute(SystemProperties.getProperty(SystemProperties.APPLICATION_MENU_ATTRIBUTE_NAME), + MenuBuilder.filterMenu(applicationMenuData, request)); + session.setAttribute(SystemProperties.getProperty(SystemProperties.BUSINESS_DIRECT_MENU_ATTRIBUTE_NAME), + MenuBuilder.filterMenu(businessDirectMenuData, request)); + } + + /** + * Creates a set of role function names and stores the set as a session + * attribute. + * + * @param allRoleFunctions + * List of role functions. + * @param session + * HttpSession + */ + private static void getAllRoleFunctions(List allRoleFunctions, HttpSession session) { + if (allRoleFunctions == null) + return; + Set roleFnSet = new HashSet(); + for (RoleFunction roleFn : allRoleFunctions) + roleFnSet.add(roleFn.getCode()); + session.setAttribute(ALL_ROLE_FUNCTIONS, roleFnSet); + } + + /** + * Removes all stored attributes from the user's session + * + * @param request + * HttpServletRequest + * @throws SessionExpiredException + * if no session exists + */ + private static void clearUserSession(HttpServletRequest request) { + HttpSession session = AppUtils.getSession(request); + if (session == null) + throw new SessionExpiredException(); + + // removes all stored attributes from the current user's session + session.removeAttribute(SystemProperties.getProperty(SystemProperties.USER_ATTRIBUTE_NAME)); + session.removeAttribute(SystemProperties.getProperty(SystemProperties.APPLICATION_MENU_ATTRIBUTE_NAME)); + session.removeAttribute(SystemProperties.getProperty(SystemProperties.BUSINESS_DIRECT_MENU_ATTRIBUTE_NAME)); + session.removeAttribute(SystemProperties.getProperty(SystemProperties.ROLES_ATTRIBUTE_NAME)); + session.removeAttribute(SystemProperties.getProperty(SystemProperties.ROLE_FUNCTIONS_ATTRIBUTE_NAME)); + } + + /** + * Builds a set of role functions and sets a session attribute with it. + * + * @param request + * HttpServletRequest + * @return Set of role functions that was built. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static Set getRoleFunctions(HttpServletRequest request) { + HashSet roleFunctions = null; + + HttpSession session = request.getSession(); + roleFunctions = (HashSet) session + .getAttribute(SystemProperties.getProperty(SystemProperties.ROLE_FUNCTIONS_ATTRIBUTE_NAME)); + + if (roleFunctions == null) { + HashMap roles = getRoles(request); + roleFunctions = new HashSet(); + + Iterator i = roles.keySet().iterator(); + + while (i.hasNext()) { + Long roleKey = (Long) i.next(); + EPRole role = (EPRole) roles.get(roleKey); + + Iterator j = role.getRoleFunctions().iterator(); + + while (j.hasNext()) { + RoleFunction function = (RoleFunction) j.next(); + roleFunctions.add(function.getCode()); + } + } + + session.setAttribute(SystemProperties.getProperty(SystemProperties.ROLE_FUNCTIONS_ATTRIBUTE_NAME), + roleFunctions); + } + + return roleFunctions; + } + + /** + * Gets role information from the user session, in the cached user object. + * As a side effect sets a session variable with the roles. + * + * @param request + * HttpServletRequest + * @return Map of role ID to role object + */ + @SuppressWarnings("rawtypes") + private static HashMap getRoles(HttpServletRequest request) { + HashMap roles = null; + + HttpSession session = AppUtils.getSession(request); + roles = (HashMap) session.getAttribute(SystemProperties.getProperty(SystemProperties.ROLES_ATTRIBUTE_NAME)); + + // if roles are not already cached, let's grab them from the user + // session + if (roles == null) { + EPUser user = getUserSession(request); + + // get all user roles (including the tree of child roles) + roles = getAllUserRoles(user); + + session.setAttribute(SystemProperties.getProperty(SystemProperties.ROLES_ATTRIBUTE_NAME), roles); + } + + return roles; + } + + /** + * Builds a map of role ID to role object. + * + * @param user + * EPUser + * @return Map of role ID to role object + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static HashMap getAllUserRoles(EPUser user) { + HashMap roles = new HashMap(); + Iterator i = user.getEPRoles().iterator(); + + while (i.hasNext()) { + EPRole role = (EPRole) i.next(); + + if (role.getActive()) { + roles.put(role.getId(), role); + + // let's take a recursive trip down the tree to add all child + // roles + addChildRoles(role, roles); + } + } + + // Additionally; the account admin role is overloaded between ecomp + // portal and partners; lets also include that + Iterator appRolesIterator = user.getEPUserApps().iterator(); + while (appRolesIterator.hasNext()) { + EPRole role = (EPRole) appRolesIterator.next().getRole(); + + if (role.getActive() && role.getId().equals(ACCOUNT_ADMIN_ROLE_ID)) { + roles.put(role.getId(), role); + + // let's take a recursive trip down the tree to add all child + // roles + addChildRoles(role, roles); + } + } + + return roles; + } + + /** + * Adds all child roles of the specified role to the map of roles. + * + * @param role + * EPRole + * @param roles + * Maps role id to role object + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static void addChildRoles(EPRole role, HashMap roles) { + Set childRoles = role.getChildRoles(); + + if (childRoles != null && childRoles.size() > 0) { + Iterator j = childRoles.iterator(); + while (j.hasNext()) { + EPRole childRole = (EPRole) j.next(); + + if (childRole.getActive()) { + roles.put(childRole.getId(), childRole); + + addChildRoles(childRole, roles); + } + } + } + + } + + public static boolean hasRole(EPUser user, String roleKey) { + return getAllUserRoles(user).keySet().contains(new Long(roleKey)); + } + + public static DataAccessService getDataAccessService() { + return dataAccessService; + } + + @Autowired + public void setDataAccessService(DataAccessService dataAccessService) { + EPUserUtils.dataAccessService = dataAccessService; + } + + /** + * Gets the user's ID from the user object in the session + * + * @param request + * HttpServletRequest + * @return Integer ID of current user + */ + public static int getUserId(HttpServletRequest request) { + return getUserIdAsLong(request).intValue(); + } + + /** + * Gets the user's ID from the user object in the session + * + * @param request + * HttpServletREquest + * @return Long ID of current user + */ + public static Long getUserIdAsLong(HttpServletRequest request) { + Long userId = new Long(SystemProperties.getProperty(SystemProperties.APPLICATION_USER_ID)); + if (request != null) { + if (getUserSession(request) != null) { + userId = getUserSession(request).getId(); + } + } + return userId; + } + + /** + * Gets the request ID from the request. + * + * @param request + * HttpServletRequest + * @return Request ID + */ + public static String getRequestId(HttpServletRequest request) { + Enumeration headerNames = request.getHeaderNames(); + + String requestId = ""; + try { + while (headerNames.hasMoreElements()) { + String headerName = (String) headerNames.nextElement(); + logger.debug(EELFLoggerDelegate.debugLogger, + "One header is " + headerName + " : " + request.getHeader(headerName)); + if (headerName.equalsIgnoreCase(SystemProperties.ECOMP_REQUEST_ID)) { + requestId = request.getHeader(headerName); + break; + } + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "HEADER!!!! Exception : " + EcompPortalUtils.getStackTrace(e)); + } + + return (requestId.isEmpty() ? UUID.randomUUID().toString() : requestId); + } + + /** + * Gets the full URL from the request. + * + * @param request + * HttpServletRequest + * @return Full URL + */ + public static String getFullURL(HttpServletRequest request) { + if (request != null) { + StringBuffer requestURL = request.getRequestURL(); + String queryString = request.getQueryString(); + + if (queryString == null) { + return requestURL.toString(); + } else { + return requestURL.append('?').append(queryString).toString(); + } + } + return ""; + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/SessionCookieUtil.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/SessionCookieUtil.java new file mode 100644 index 00000000..1ef44a44 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/SessionCookieUtil.java @@ -0,0 +1,129 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.util; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalapp.portal.utils.EcompPortalUtils; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.onboarding.util.PortalApiConstants; +import org.openecomp.portalsdk.core.onboarding.listener.PortalTimeoutHandler; +import org.openecomp.portalsdk.core.onboarding.util.CipherUtil; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalsdk.core.web.support.AppUtils; + +public class SessionCookieUtil { + + //private static final String JSESSIONID = "JSESSIONID"; + private static final String EP_SERVICE = "EPService"; + private static final String USER_ID = "UserId"; + private static Integer cookieMaxAge = -1; + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SessionCookieUtil.class); + + public static void preSetUp(HttpServletRequest request, + HttpServletResponse response) { + initateSessionMgtHandler(request); + //set up EPService cookie + setUpEPServiceCookie(request, response); + } + + public static void setUpEPServiceCookie(HttpServletRequest request, + HttpServletResponse response) { + String jSessionId = getJessionId(request); + Cookie cookie1 = new Cookie(EP_SERVICE, jSessionId); + cookie1.setMaxAge(cookieMaxAge); + cookie1.setDomain(EPCommonSystemProperties.getProperty(EPCommonSystemProperties.COOKIE_DOMAIN)); + cookie1.setPath("/"); + response.addCookie(cookie1); + } + + public static void setUpUserIdCookie(HttpServletRequest request, + HttpServletResponse response,String userId) throws Exception { + logger.info("************** session cookie util set up UserId cookie begins"); + userId = CipherUtil.encrypt(userId, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + Cookie cookie1 = new Cookie(USER_ID, userId); + cookie1.setMaxAge(cookieMaxAge); + cookie1.setDomain(EPCommonSystemProperties.getProperty(EPCommonSystemProperties.COOKIE_DOMAIN)); + cookie1.setPath("/"); + response.addCookie(cookie1); + logger.info("************** session cookie util set up EP cookie completed"); + } + + public static String getUserIdFromCookie(HttpServletRequest request, + HttpServletResponse response) throws Exception { + String userId = ""; + Cookie[] cookies = request.getCookies(); + Cookie userIdcookie = null; + if (cookies != null) + for (Cookie cookie : cookies) + if (cookie.getName().equals(USER_ID)) + userIdcookie = cookie; + if(userIdcookie!=null){ + userId = CipherUtil.decrypt(userIdcookie.getValue(), + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } + + logger.info("************** session cookie util set up EP cookie completed"); + return userId; + } + + public static String getJessionId(HttpServletRequest request){ + + return request.getSession().getId(); + /* + Cookie ep = WebUtils.getCookie(request, JSESSIONID); + if(ep==null){ + return request.getSession().getId(); + } + return ep.getValue(); + */ + } + + protected static void initateSessionMgtHandler(HttpServletRequest request) { + String jSessionId = getJessionId(request); + storeMaxInactiveTime(request); + PortalTimeoutHandler.sessionCreated(jSessionId, jSessionId, AppUtils.getSession(request)); + } + + protected static void storeMaxInactiveTime(HttpServletRequest request) { + HttpSession session = AppUtils.getSession(request); + if(session.getAttribute(PortalApiConstants.GLOBAL_SESSION_MAX_IDLE_TIME) == null) + session.setAttribute(PortalApiConstants.GLOBAL_SESSION_MAX_IDLE_TIME,session.getMaxInactiveInterval()); + } + + public static void resetSessionMaxIdleTimeOut(HttpServletRequest request) { + try { + HttpSession session = AppUtils.getSession(request); + final Object maxIdleAttribute = session.getAttribute(PortalApiConstants.GLOBAL_SESSION_MAX_IDLE_TIME); + if(session != null && maxIdleAttribute != null) { + session.setMaxInactiveInterval(Integer.parseInt(maxIdleAttribute.toString())); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "Could not reset the session timeout. Details: " + EcompPortalUtils.getStackTrace(e)); + } + + } + +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/SystemType.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/SystemType.java new file mode 100644 index 00000000..fb0e35fc --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalapp/util/SystemType.java @@ -0,0 +1,26 @@ +/*- + * ================================================================================ + * ECOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalapp.util; + +public enum SystemType { + + APPLICATION, +// SYSTEM +} diff --git a/ecomp-portal-BE-common/src/main/java/org/openecomp/portalsdk/core/lm/FusionLicenseManagerUtils.java b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalsdk/core/lm/FusionLicenseManagerUtils.java new file mode 100644 index 00000000..72461206 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/openecomp/portalsdk/core/lm/FusionLicenseManagerUtils.java @@ -0,0 +1,75 @@ +/*- + * ================================================================================ + * ECOMP Portal SDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.portalsdk.core.lm; + +import java.util.Date; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; + +import org.openecomp.portalapp.portal.utils.EPCommonSystemProperties; +import org.openecomp.portalsdk.core.util.SystemProperties; +//import org.openecomp.portalapp.lm.FusionLicenseManagerImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.support.RequestContextUtils; + +@Component +public class FusionLicenseManagerUtils { + + @Autowired + private FusionLicenseManager licenseManager; + + @Autowired + private EPCommonSystemProperties sysProps; + static { + } + + public int verifyLicense(ServletContext context) { + if(sysProps == null) { + try { + sysProps = new EPCommonSystemProperties(); + sysProps.setServletContext(context); +// sysProps.load(); +// licenseManager = new FusionLicenseManagerImpl(new LicenseableClassImpl()); + System.out.println(licenseManager); + licenseManager.installLicense(); + } + catch(Exception ex) { + ex.printStackTrace(); + } + } + + + +// WebApplicationContext ctx =RequestContextUtils.getWebApplicationContext(request); +// int flag = ((FusionLicenseManager)ctx.getBean("fusionLicenseManager")).verifyLicense(request); +// logger.debug("****************FLAG ******************** " + flag); +// return flag; +// return 2; + return licenseManager.verifyLicense(context); + } + + public static Date getLicenseExpiryDate(HttpServletRequest request) { + WebApplicationContext ctx =RequestContextUtils.getWebApplicationContext(request); + return ((FusionLicenseManager)ctx.getBean("fusionLicenseManager")).getExpiredDate(); + } +} diff --git a/ecomp-portal-BE-common/src/main/webapp/META-INF/MANIFEST.MF b/ecomp-portal-BE-common/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 00000000..254272e1 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/defs/definitions.xml b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/defs/definitions.xml new file mode 100644 index 00000000..18445f20 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/defs/definitions.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/defs/definitions.xml b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/defs/definitions.xml new file mode 100644 index 00000000..433fec8a --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/defs/definitions.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/jsp/.gitignore b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/jsp/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/EP.hbm.xml b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/EP.hbm.xml new file mode 100644 index 00000000..bda281c2 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/EP.hbm.xml @@ -0,0 +1,1595 @@ + + + + + + + + + + + + seq_fn_widget + + + + + + + + + + + + + + + + + + + + + + + + + seq_ep_endpoint + + + + + + + + + + + + seq_ep_endpoints_basic_auth_account + + + + + + + + + + + + + + seq_ep_widget_catalog_parameter + + + + + + + + + + + + + + + + + + + + + + seq_ep_basic_auth_account + + + + + + + + + + + + + + + + + + + + + seq_ep_user_notification + + + + + + + + + + + + + + seq_fn_user + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + seq_ep_microservice + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + seq_ep_microservice_parameter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + seq_fn_app + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + seq_ep_usrappsrtprf + + + + + + + + + + + seq_ep_usrmanappsrtprf + + + + + + + + + + + seq_ep_usrwidgetsrtprf + + + + + + + + + + + + + + + seq_fn_role + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + app + + + + + + + + + + + + + + + + + seq_fn_pers_user_app_sel + + + + + + + + + + + seq_ep_pers_user_widget_sel + + + + + + + + + + + + seq_ep_user_role_request + + + + + + + + + + + + + + + + + + + seq_ep_user_role_request_det + + + + + + + + + + + + + + + + + + + 'Y' + and (upper(d.wdg_name) like upper(CONCAT('%', :searchQuery, '%')) or upper(d.wdg_desc) like upper(CONCAT('%', :searchQuery, '%'))) + ) + union + ( + select distinct 'Widget' CATEGORY, d.wdg_name NAME, + d.widget_id UUID, + d.wdg_desc TARGET + from ep_widget_catalog d + where d.all_user_flag='Y' + and (upper(d.wdg_name) like upper(CONCAT('%', :searchQuery, '%')) or upper(d.wdg_desc) like upper(CONCAT('%', :searchQuery, '%'))) + ) + union + ( + select distinct 'Widget' CATEGORY, d.wdg_name NAME, + d.widget_id UUID, + d.wdg_desc TARGET + from fn_user a, fn_user_role b, ep_widget_catalog d + where upper(a.org_user_id) = upper( :userId ) + and a.user_id = b.user_id + and b.role_id = 1 + and (upper(d.wdg_name) like upper(CONCAT('%', :searchQuery, '%')) or upper(d.wdg_desc) like upper(CONCAT('%', :searchQuery, '%'))) + ) + ) y + ) t, + (SELECT @rn /*'*/:=/*'*/ 0) t2 order by NAME + ; + + ]]> + + + + + + = start_time) + and a.is_for_all_roles = 'N' + ) a, + ( + select a.user_id, c.role_id, c.app_id, d.APP_NAME + from fn_user a, fn_user_role b, fn_role c, fn_app d + where a.user_id = b.user_id + and a.user_id = :user_id + and b.role_id = c.role_id + and c.app_id = d.app_id + and d.enabled='Y' + )b + where + ( + a.role_id = b.role_id||a.recv_user_id=:user_id + ) + union + ( + select :user_id user_id, notification_id, is_for_online_users, is_for_all_roles, msg_header, msg_description, msg_source,start_Time, end_time, priority, created_date, creator_ID,active_YN + from ep_notification a + where a.notification_id + and (end_time is null || SYSDATE() <= end_time ) + and (start_time is null || SYSDATE() >= start_time) + and a.is_for_all_roles = 'Y' + ) + ) a + where + active_YN = 'Y' + and + not exists + ( + select * from ep_user_notification m where user_id = :user_id and m.notification_id = a.notification_id and is_viewed = 'Y' + ) + order by priority desc, start_Time desc,created_date desc + + + ) t, + (SELECT @rn /*'*/:=/*'*/ 0) t2 + ; + ]]> + + + + + + + + + + + + + + + + + + + + 1) b +union +select fn_role.app_id,fn_app.app_name, fn_role.role_id ,fn_role.role_name +from fn_app, fn_role +where fn_role.app_id = fn_app.app_id and fn_app.enabled='Y' order by app_name + + + + ]]> + + + + + + = DATE_ADD(curdate(),INTERVAL-31 DAY)) + or + (start_time is not null and end_time is null and start_time >= DATE_ADD(curdate(),INTERVAL -31 DAY)) + or + (start_time is null and end_time is not null and end_time >= DATE_ADD(curdate(),INTERVAL -31 DAY)) + or + (start_time is not null and end_time is not null and end_time >= DATE_ADD(curdate(),INTERVAL -31 DAY)) + ) + ) a, + ( + select a.user_id, c.role_id, c.app_id, d.APP_NAME + from fn_user a, fn_user_role b, fn_role c, fn_app d + where a.user_id = b.user_id + and a.user_id = :user_id + and b.role_id = c.role_id + and c.app_id = d.app_id + and d.enabled='Y' + ) b + where + ( + a.role_id = b.role_id||a.recv_user_id=:user_id + ) + union + ( + select + :user_id user_id, b.login_id,notification_id, is_for_online_users, is_for_all_roles, + msg_header, msg_description,msg_source, start_Time, end_time, priority, a.created_date, + creator_ID, a.active_YN + from ep_notification a JOIN fn_user b on b.user_id=a.creator_ID + where a.notification_id + and a.is_for_all_roles = 'Y' + and ( + (start_time is null and end_time is null and a.created_date >= DATE_ADD(curdate(),INTERVAL-31 DAY)) + or + (start_time is not null and end_time is null and start_time >= DATE_ADD(curdate(),INTERVAL -31 DAY)) + or + (start_time is null and end_time is not null and end_time >= DATE_ADD(curdate(),INTERVAL -31 DAY)) + or + (start_time is not null and end_time is not null and end_time >= DATE_ADD(curdate(),INTERVAL -31 DAY)) + ) + ) + ) a left outer join ( + select m.notification_ID, m.is_viewed from ep_user_notification m where user_id = :user_id + ) m + on a.notification_id = m.notification_ID + where + active_YN = 'Y' + + order by start_Time desc,end_time desc + ) t, + (SELECT @rn /*'*/:=/*'*/ 0) t2 where startTime<=SYSDATE() + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + from CommonWidget where category = :cat order by + sortOrder, title + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/Fusion.hbm.xml b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/Fusion.hbm.xml new file mode 100644 index 00000000..f6d22a27 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/Fusion.hbm.xml @@ -0,0 +1,335 @@ + + + + + + + + + + + + + seq_fn_user + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + seq_fn_role + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + seq_fn_menu + + + + + + + + + + + + + + + + + + + + + + + seq_fn_menu + + + + + + + + + + + + + + + + + + + + + + + + + + + + seq_fn_broadcast_message + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + seq_fn_audit_log + + + + + + + + + + + + + + + + + + + + + + + + + select distinct functionCd from MenuData + + + + from MenuData where menuSetCode = :menu_set_cd and parentMenu is null + + + FROM UrlsAccessible A where upper(A.urlsAccessibleKey.url) = upper(:current_url) + + + + select firstName, lastName from User where id = :user_id + + + + select email from User where id = :user_id + + + + select id, firstName, lastName from User where active = true order by lastName, firstName + + + + select name from Role where id = :role_id + + + + select id, name from Role order by name + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/Workflow.hbm.xml b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/Workflow.hbm.xml new file mode 100644 index 00000000..9a853e7f --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/fusion/orm/Workflow.hbm.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/error.jsp b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/error.jsp new file mode 100644 index 00000000..0505b68c --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/error.jsp @@ -0,0 +1,20 @@ +<%-- + ================================================================================ + ECOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --%> +${errMsg} diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/index.jsp b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/index.jsp new file mode 100644 index 00000000..39437e78 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/index.jsp @@ -0,0 +1,21 @@ +<%-- + ================================================================================ + ECOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/login.jsp b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/login.jsp new file mode 100644 index 00000000..e10ba624 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/login.jsp @@ -0,0 +1,120 @@ +<%-- + ================================================================================ + ECOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --%> +<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page import="java.net.URLEncoder" %> +<%@ page import="org.openecomp.portalsdk.core.util.SystemProperties" %> + + + + + + + + + + + + + + + + + + + + <% + String returnUrl = request.getParameter("returnUrl"); + String redirectUrl = request.getParameter("redirectUrl"); + returnUrl = ((returnUrl == null) ? (request.isSecure() ?"https://":"http://") + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/process_csp" + "?redirectUrl=" + redirectUrl: URLEncoder.encode(returnUrl)); + %> + + +
+ +
+ AT&T +
+
+
+
+
+ +
+ + Click here to login + +
+
+
+
+
+







+ +
+ + + + diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/login_external.jsp b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/login_external.jsp new file mode 100644 index 00000000..fe5eef06 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/jsp/login_external.jsp @@ -0,0 +1,162 @@ +<%-- + ================================================================================ + ECOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --%> +<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ page import="org.openecomp.portalsdk.core.util.SystemProperties" %> + +" /> + + + + + + Login + + + + + + + + + + + + + + + + + +
+ +
+ AT&T +
+
+ +
+
+ Portal +

ECOMP Portal

+ + +
+
+ + +
+
+                + +
+
+
+






+ +
+ + + + diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/angular-sanitize.js b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/angular-sanitize.js new file mode 100644 index 00000000..87100fdb --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/angular-sanitize.js @@ -0,0 +1,647 @@ +/** + * @license AngularJS v1.2.25 + * (c) 2010-2014 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) {'use strict'; + +var $sanitizeMinErr = angular.$$minErr('$sanitize'); + +/** + * @ngdoc module + * @name ngSanitize + * @description + * + * # ngSanitize + * + * The `ngSanitize` module provides functionality to sanitize HTML. + * + * + *
+ * + * See {@link ngSanitize.$sanitize `$sanitize`} for usage. + */ + +/* + * HTML Parser By Misko Hevery (misko@hevery.com) + * based on: HTML Parser By John Resig (ejohn.org) + * Original code by Erik Arvidsson, Mozilla Public License + * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js + * + * // Use like so: + * htmlParser(htmlString, { + * start: function(tag, attrs, unary) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * }); + * + */ + + +/** + * @ngdoc service + * @name $sanitize + * @kind function + * + * @description + * The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are + * then serialized back to properly escaped html string. This means that no unsafe input can make + * it into the returned string, however, since our parser is more strict than a typical browser + * parser, it's possible that some obscure input, which would be recognized as valid HTML by a + * browser, won't make it through the sanitizer. + * The whitelist is configured using the functions `aHrefSanitizationWhitelist` and + * `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}. + * + * @param {string} html Html input. + * @returns {string} Sanitized html. + * + * @example + + + +
+ Snippet: + + + + + + + + + + + + + + + + + + + + + + + + + +
DirectiveHowSourceRendered
ng-bind-htmlAutomatically uses $sanitize
<div ng-bind-html="snippet">
</div>
ng-bind-htmlBypass $sanitize by explicitly trusting the dangerous value +
<div ng-bind-html="deliberatelyTrustDangerousSnippet()">
+</div>
+
ng-bindAutomatically escapes
<div ng-bind="snippet">
</div>
+
+
+ + it('should sanitize the html snippet by default', function() { + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('

an html\nclick here\nsnippet

'); + }); + + it('should inline raw snippet if bound to a trusted value', function() { + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()). + toBe("

an html\n" + + "click here\n" + + "snippet

"); + }); + + it('should escape snippet without any filter', function() { + expect(element(by.css('#bind-default div')).getInnerHtml()). + toBe("<p style=\"color:blue\">an html\n" + + "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + + "snippet</p>"); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new text'); + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('new text'); + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe( + 'new text'); + expect(element(by.css('#bind-default div')).getInnerHtml()).toBe( + "new <b onclick=\"alert(1)\">text</b>"); + }); +
+
+ */ +function $SanitizeProvider() { + this.$get = ['$$sanitizeUri', function($$sanitizeUri) { + return function(html) { + var buf = []; + htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) { + return !/^unsafe/.test($$sanitizeUri(uri, isImage)); + })); + return buf.join(''); + }; + }]; +} + +function sanitizeText(chars) { + var buf = []; + var writer = htmlSanitizeWriter(buf, angular.noop); + writer.chars(chars); + return buf.join(''); +} + + +// Regular Expressions for parsing tags and attributes +var START_TAG_REGEXP = + /^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/, + END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/, + ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, + BEGIN_TAG_REGEXP = /^/g, + DOCTYPE_REGEXP = /]*?)>/i, + CDATA_REGEXP = //g, + SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, + // Match everything outside of normal chars and " (quote character) + NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; + + +// Good source of info about elements and attributes +// http://dev.w3.org/html5/spec/Overview.html#semantics +// http://simon.html5.org/html-elements + +// Safe Void Elements - HTML5 +// http://dev.w3.org/html5/spec/Overview.html#void-elements +var voidElements = makeMap("area,br,col,hr,img,wbr"); + +// Elements that you can, intentionally, leave open (and which close themselves) +// http://dev.w3.org/html5/spec/Overview.html#optional-tags +var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), + optionalEndTagInlineElements = makeMap("rp,rt"), + optionalEndTagElements = angular.extend({}, + optionalEndTagInlineElements, + optionalEndTagBlockElements); + +// Safe Block Elements - HTML5 +var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," + + "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," + + "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")); + +// Inline Elements - HTML5 +var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," + + "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," + + "samp,small,span,strike,strong,sub,sup,time,tt,u,var")); + + +// Special Elements (can contain anything) +var specialElements = makeMap("script,style"); + +var validElements = angular.extend({}, + voidElements, + blockElements, + inlineElements, + optionalEndTagElements); + +//Attributes that have href and hence need to be sanitized +var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap"); +var validAttrs = angular.extend({}, uriAttrs, makeMap( + 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ + 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ + 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ + 'scope,scrolling,shape,size,span,start,summary,target,title,type,'+ + 'valign,value,vspace,width')); + +function makeMap(str) { + var obj = {}, items = str.split(','), i; + for (i = 0; i < items.length; i++) obj[items[i]] = true; + return obj; +} + + +/** + * @example + * htmlParser(htmlString, { + * start: function(tag, attrs, unary) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * }); + * + * @param {string} html string + * @param {object} handler + */ +function htmlParser( html, handler ) { + if (typeof html !== 'string') { + if (html === null || typeof html === 'undefined') { + html = ''; + } else { + html = '' + html; + } + } + var index, chars, match, stack = [], last = html, text; + stack.last = function() { return stack[ stack.length - 1 ]; }; + + while ( html ) { + text = ''; + chars = true; + + // Make sure we're not in a script or style element + if ( !stack.last() || !specialElements[ stack.last() ] ) { + + // Comment + if ( html.indexOf("", index) === index) { + if (handler.comment) handler.comment( html.substring( 4, index ) ); + html = html.substring( index + 3 ); + chars = false; + } + // DOCTYPE + } else if ( DOCTYPE_REGEXP.test(html) ) { + match = html.match( DOCTYPE_REGEXP ); + + if ( match ) { + html = html.replace( match[0], ''); + chars = false; + } + // end tag + } else if ( BEGING_END_TAGE_REGEXP.test(html) ) { + match = html.match( END_TAG_REGEXP ); + + if ( match ) { + html = html.substring( match[0].length ); + match[0].replace( END_TAG_REGEXP, parseEndTag ); + chars = false; + } + + // start tag + } else if ( BEGIN_TAG_REGEXP.test(html) ) { + match = html.match( START_TAG_REGEXP ); + + if ( match ) { + // We only have a valid start-tag if there is a '>'. + if ( match[4] ) { + html = html.substring( match[0].length ); + match[0].replace( START_TAG_REGEXP, parseStartTag ); + } + chars = false; + } else { + // no ending tag found --- this piece should be encoded as an entity. + text += '<'; + html = html.substring(1); + } + } + + if ( chars ) { + index = html.indexOf("<"); + + text += index < 0 ? html : html.substring( 0, index ); + html = index < 0 ? "" : html.substring( index ); + + if (handler.chars) handler.chars( decodeEntities(text) ); + } + + } else { + html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), + function(all, text){ + text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1"); + + if (handler.chars) handler.chars( decodeEntities(text) ); + + return ""; + }); + + parseEndTag( "", stack.last() ); + } + + if ( html == last ) { + throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " + + "of html: {0}", html); + } + last = html; + } + + // Clean up any remaining tags + parseEndTag(); + + function parseStartTag( tag, tagName, rest, unary ) { + tagName = angular.lowercase(tagName); + if ( blockElements[ tagName ] ) { + while ( stack.last() && inlineElements[ stack.last() ] ) { + parseEndTag( "", stack.last() ); + } + } + + if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) { + parseEndTag( "", tagName ); + } + + unary = voidElements[ tagName ] || !!unary; + + if ( !unary ) + stack.push( tagName ); + + var attrs = {}; + + rest.replace(ATTR_REGEXP, + function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) { + var value = doubleQuotedValue + || singleQuotedValue + || unquotedValue + || ''; + + attrs[name] = decodeEntities(value); + }); + if (handler.start) handler.start( tagName, attrs, unary ); + } + + function parseEndTag( tag, tagName ) { + var pos = 0, i; + tagName = angular.lowercase(tagName); + if ( tagName ) + // Find the closest opened tag of the same type + for ( pos = stack.length - 1; pos >= 0; pos-- ) + if ( stack[ pos ] == tagName ) + break; + + if ( pos >= 0 ) { + // Close all the open elements, up the stack + for ( i = stack.length - 1; i >= pos; i-- ) + if (handler.end) handler.end( stack[ i ] ); + + // Remove the open elements from the stack + stack.length = pos; + } + } +} + +var hiddenPre=document.createElement("pre"); +var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/; +/** + * decodes all entities into regular string + * @param value + * @returns {string} A string with decoded entities. + */ +function decodeEntities(value) { + if (!value) { return ''; } + + // Note: IE8 does not preserve spaces at the start/end of innerHTML + // so we must capture them and reattach them afterward + var parts = spaceRe.exec(value); + var spaceBefore = parts[1]; + var spaceAfter = parts[3]; + var content = parts[2]; + if (content) { + hiddenPre.innerHTML=content.replace(//g, '>'); +} + +/** + * create an HTML/XML writer which writes to buffer + * @param {Array} buf use buf.jain('') to get out sanitized html string + * @returns {object} in the form of { + * start: function(tag, attrs, unary) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * } + */ +function htmlSanitizeWriter(buf, uriValidator){ + var ignore = false; + var out = angular.bind(buf, buf.push); + return { + start: function(tag, attrs, unary){ + tag = angular.lowercase(tag); + if (!ignore && specialElements[tag]) { + ignore = tag; + } + if (!ignore && validElements[tag] === true) { + out('<'); + out(tag); + angular.forEach(attrs, function(value, key){ + var lkey=angular.lowercase(key); + var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); + if (validAttrs[lkey] === true && + (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { + out(' '); + out(key); + out('="'); + out(encodeEntities(value)); + out('"'); + } + }); + out(unary ? '/>' : '>'); + } + }, + end: function(tag){ + tag = angular.lowercase(tag); + if (!ignore && validElements[tag] === true) { + out(''); + } + if (tag == ignore) { + ignore = false; + } + }, + chars: function(chars){ + if (!ignore) { + out(encodeEntities(chars)); + } + } + }; +} + + +// define ngSanitize module and register $sanitize service +angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider); + +/* global sanitizeText: false */ + +/** + * @ngdoc filter + * @name linky + * @kind function + * + * @description + * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and + * plain email address links. + * + * Requires the {@link ngSanitize `ngSanitize`} module to be installed. + * + * @param {string} text Input text. + * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in. + * @returns {string} Html-linkified text. + * + * @usage + + * + * @example + + + +
+ Snippet: + + + + + + + + + + + + + + + + + + + + + +
FilterSourceRendered
linky filter +
<div ng-bind-html="snippet | linky">
</div>
+
+
+
linky target +
<div ng-bind-html="snippetWithTarget | linky:'_blank'">
</div>
+
+
+
no filter
<div ng-bind="snippet">
</div>
+ + + it('should linkify the snippet with urls', function() { + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(4); + }); + + it('should not linkify snippet without the linky filter', function() { + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#escaped-html a')).count()).toEqual(0); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new http://link.'); + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('new http://link.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(1); + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()) + .toBe('new http://link.'); + }); + + it('should work with the target property', function() { + expect(element(by.id('linky-target')). + element(by.binding("snippetWithTarget | linky:'_blank'")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank'); + }); + + + */ +angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) { + var LINKY_URL_REGEXP = + /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"]/, + MAILTO_REGEXP = /^mailto:/; + + return function(text, target) { + if (!text) return text; + var match; + var raw = text; + var html = []; + var url; + var i; + while ((match = raw.match(LINKY_URL_REGEXP))) { + // We can not end in these as they are sometimes found at the end of the sentence + url = match[0]; + // if we did not match ftp/http/mailto then assume mailto + if (match[2] == match[3]) url = 'mailto:' + url; + i = match.index; + addText(raw.substr(0, i)); + addLink(url, match[0].replace(MAILTO_REGEXP, '')); + raw = raw.substring(i + match[0].length); + } + addText(raw); + return $sanitize(html.join('')); + + function addText(text) { + if (!text) { + return; + } + html.push(sanitizeText(text)); + } + + function addLink(url, text) { + html.push(''); + addText(text); + html.push(''); + } + }; +}]); + + +})(window, window.angular); diff --git a/ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/angular.js b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/angular.js new file mode 100644 index 00000000..132234b6 --- /dev/null +++ b/ecomp-portal-BE-common/src/main/webapp/WEB-INF/static/ebz/angular_js/angular.js @@ -0,0 +1,22024 @@ +/** + * @license AngularJS v1.2.25 + * (c) 2010-2014 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, document, undefined) {'use strict'; + +/** + * @description + * + * This object provides a utility for producing rich Error messages within + * Angular. It can be called as follows: + * + * var exampleMinErr = minErr('example'); + * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); + * + * The above creates an instance of minErr in the example namespace. The + * resulting error will have a namespaced error code of example.one. The + * resulting error will replace {0} with the value of foo, and {1} with the + * value of bar. The object is not restricted in the number of arguments it can + * take. + * + * If fewer arguments are specified than necessary for interpolation, the extra + * interpolation markers will be preserved in the final string. + * + * Since data will be parsed statically during a build step, some restrictions + * are applied with respect to how minErr instances are created and called. + * Instances should have names of the form namespaceMinErr for a minErr created + * using minErr('namespace') . Error codes, namespaces and template strings + * should all be static strings, not variables or general expressions. + * + * @param {string} module The namespace to use for the new minErr instance. + * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance + */ + +function minErr(module) { + return function () { + var code = arguments[0], + prefix = '[' + (module ? module + ':' : '') + code + '] ', + template = arguments[1], + templateArgs = arguments, + stringify = function (obj) { + if (typeof obj === 'function') { + return obj.toString().replace(/ \{[\s\S]*$/, ''); + } else if (typeof obj === 'undefined') { + return 'undefined'; + } else if (typeof obj !== 'string') { + return JSON.stringify(obj); + } + return obj; + }, + message, i; + + message = prefix + template.replace(/\{\d+\}/g, function (match) { + var index = +match.slice(1, -1), arg; + + if (index + 2 < templateArgs.length) { + arg = templateArgs[index + 2]; + if (typeof arg === 'function') { + return arg.toString().replace(/ ?\{[\s\S]*$/, ''); + } else if (typeof arg === 'undefined') { + return 'undefined'; + } else if (typeof arg !== 'string') { + return toJson(arg); + } + return arg; + } + return match; + }); + + message = message + '\nhttp://errors.angularjs.org/1.2.25/' + + (module ? module + '/' : '') + code; + for (i = 2; i < arguments.length; i++) { + message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + + encodeURIComponent(stringify(arguments[i])); + } + + return new Error(message); + }; +} + +/* We need to tell jshint what variables are being exported */ +/* global angular: true, + msie: true, + jqLite: true, + jQuery: true, + slice: true, + push: true, + toString: true, + ngMinErr: true, + angularModule: true, + nodeName_: true, + uid: true, + VALIDITY_STATE_PROPERTY: true, + + lowercase: true, + uppercase: true, + manualLowercase: true, + manualUppercase: true, + nodeName_: true, + isArrayLike: true, + forEach: true, + sortedKeys: true, + forEachSorted: true, + reverseParams: true, + nextUid: true, + setHashKey: true, + extend: true, + int: true, + inherit: true, + noop: true, + identity: true, + valueFn: true, + isUndefined: true, + isDefined: true, + isObject: true, + isString: true, + isNumber: true, + isDate: true, + isArray: true, + isFunction: true, + isRegExp: true, + isWindow: true, + isScope: true, + isFile: true, + isBlob: true, + isBoolean: true, + isPromiseLike: true, + trim: true, + isElement: true, + makeMap: true, + map: true, + size: true, + includes: true, + indexOf: true, + arrayRemove: true, + isLeafNode: true, + copy: true, + shallowCopy: true, + equals: true, + csp: true, + concat: true, + sliceArgs: true, + bind: true, + toJsonReplacer: true, + toJson: true, + fromJson: true, + toBoolean: true, + startingTag: true, + tryDecodeURIComponent: true, + parseKeyValue: true, + toKeyValue: true, + encodeUriSegment: true, + encodeUriQuery: true, + angularInit: true, + bootstrap: true, + snake_case: true, + bindJQuery: true, + assertArg: true, + assertArgFn: true, + assertNotHasOwnProperty: true, + getter: true, + getBlockElements: true, + hasOwnProperty: true, +*/ + +//////////////////////////////////// + +/** + * @ngdoc module + * @name ng + * @module ng + * @description + * + * # ng (core module) + * The ng module is loaded by default when an AngularJS application is started. The module itself + * contains the essential components for an AngularJS application to function. The table below + * lists a high level breakdown of each of the services/factories, filters, directives and testing + * components available within this core module. + * + *
+ */ + +// The name of a form control's ValidityState property. +// This is used so that it's possible for internal tests to create mock ValidityStates. +var VALIDITY_STATE_PROPERTY = 'validity'; + +/** + * @ngdoc function + * @name angular.lowercase + * @module ng + * @kind function + * + * @description Converts the specified string to lowercase. + * @param {string} string String to be converted to lowercase. + * @returns {string} Lowercased string. + */ +var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;}; +var hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * @ngdoc function + * @name angular.uppercase + * @module ng + * @kind function + * + * @description Converts the specified string to uppercase. + * @param {string} string String to be converted to uppercase. + * @returns {string} Uppercased string. + */ +var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;}; + + +var manualLowercase = function(s) { + /* jshint bitwise: false */ + return isString(s) + ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) + : s; +}; +var manualUppercase = function(s) { + /* jshint bitwise: false */ + return isString(s) + ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) + : s; +}; + + +// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish +// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods +// with correct but slower alternatives. +if ('i' !== 'I'.toLowerCase()) { + lowercase = manualLowercase; + uppercase = manualUppercase; +} + + +var /** holds major version number for IE or NaN for real browsers */ + msie, + jqLite, // delay binding since jQuery could be loaded after us. + jQuery, // delay binding + slice = [].slice, + push = [].push, + toString = Object.prototype.toString, + ngMinErr = minErr('ng'), + + /** @name angular */ + angular = window.angular || (window.angular = {}), + angularModule, + nodeName_, + uid = ['0', '0', '0']; + +/** + * IE 11 changed the format of the UserAgent string. + * See http://msdn.microsoft.com/en-us/library/ms537503.aspx + */ +msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); +if (isNaN(msie)) { + msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); +} + + +/** + * @private + * @param {*} obj + * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, + * String ...) + */ +function isArrayLike(obj) { + if (obj == null || isWindow(obj)) { + return false; + } + + var length = obj.length; + + if (obj.nodeType === 1 && length) { + return true; + } + + return isString(obj) || isArray(obj) || length === 0 || + typeof length === 'number' && length > 0 && (length - 1) in obj; +} + +/** + * @ngdoc function + * @name angular.forEach + * @module ng + * @kind function + * + * @description + * Invokes the `iterator` function once for each item in `obj` collection, which can be either an + * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` + * is the value of an object property or an array element and `key` is the object property key or + * array element index. Specifying a `context` for the function is optional. + * + * It is worth noting that `.forEach` does not iterate over inherited properties because it filters + * using the `hasOwnProperty` method. + * + ```js + var values = {name: 'misko', gender: 'male'}; + var log = []; + angular.forEach(values, function(value, key) { + this.push(key + ': ' + value); + }, log); + expect(log).toEqual(['name: misko', 'gender: male']); + ``` + * + * @param {Object|Array} obj Object to iterate over. + * @param {Function} iterator Iterator function. + * @param {Object=} context Object to become context (`this`) for the iterator function. + * @returns {Object|Array} Reference to `obj`. + */ +function forEach(obj, iterator, context) { + var key; + if (obj) { + if (isFunction(obj)) { + for (key in obj) { + // Need to check if hasOwnProperty exists, + // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function + if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { + iterator.call(context, obj[key], key); + } + } + } else if (isArray(obj) || isArrayLike(obj)) { + for (key = 0; key < obj.length; key++) { + iterator.call(context, obj[key], key); + } + } else if (obj.forEach && obj.forEach !== forEach) { + obj.forEach(iterator, context); + } else { + for (key in obj) { + if (obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key); + } + } + } + } + return obj; +} + +function sortedKeys(obj) { + var keys = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + keys.push(key); + } + } + return keys.sort(); +} + +function forEachSorted(obj, iterator, context) { + var keys = sortedKeys(obj); + for ( var i = 0; i < keys.length; i++) { + iterator.call(context, obj[keys[i]], keys[i]); + } + return keys; +} + + +/** + * when using forEach the params are value, key, but it is often useful to have key, value. + * @param {function(string, *)} iteratorFn + * @returns {function(*, string)} + */ +function reverseParams(iteratorFn) { + return function(value, key) { iteratorFn(key, value); }; +} + +/** + * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric + * characters such as '012ABC'. The reason why we are not using simply a number counter is that + * the number string gets longer over time, and it can also overflow, where as the nextId + * will grow much slower, it is a string, and it will never overflow. + * + * @returns {string} an unique alpha-numeric string + */ +function nextUid() { + var index = uid.length; + var digit; + + while(index) { + index--; + digit = uid[index].charCodeAt(0); + if (digit == 57 /*'9'*/) { + uid[index] = 'A'; + return uid.join(''); + } + if (digit == 90 /*'Z'*/) { + uid[index] = '0'; + } else { + uid[index] = String.fromCharCode(digit + 1); + return uid.join(''); + } + } + uid.unshift('0'); + return uid.join(''); +} + + +/** + * Set or clear the hashkey for an object. + * @param obj object + * @param h the hashkey (!truthy to delete the hashkey) + */ +function setHashKey(obj, h) { + if (h) { + obj.$$hashKey = h; + } + else { + delete obj.$$hashKey; + } +} + +/** + * @ngdoc function + * @name angular.extend + * @module ng + * @kind function + * + * @description + * Extends the destination object `dst` by copying all of the properties from the `src` object(s) + * to `dst`. You can specify multiple `src` objects. + * + * @param {Object} dst Destination object. + * @param {...Object} src Source object(s). + * @returns {Object} Reference to `dst`. + */ +function extend(dst) { + var h = dst.$$hashKey; + forEach(arguments, function(obj) { + if (obj !== dst) { + forEach(obj, function(value, key) { + dst[key] = value; + }); + } + }); + + setHashKey(dst,h); + return dst; +} + +function int(str) { + return parseInt(str, 10); +} + + +function inherit(parent, extra) { + return extend(new (extend(function() {}, {prototype:parent}))(), extra); +} + +/** + * @ngdoc function + * @name angular.noop + * @module ng + * @kind function + * + * @description + * A function that performs no operations. This function can be useful when writing code in the + * functional style. + ```js + function foo(callback) { + var result = calculateResult(); + (callback || angular.noop)(result); + } + ``` + */ +function noop() {} +noop.$inject = []; + + +/** + * @ngdoc function + * @name angular.identity + * @module ng + * @kind function + * + * @description + * A function that returns its first argument. This function is useful when writing code in the + * functional style. + * + ```js + function transformer(transformationFn, value) { + return (transformationFn || angular.identity)(value); + }; + ``` + */ +function identity($) {return $;} +identity.$inject = []; + + +function valueFn(value) {return function() {return value;};} + +/** + * @ngdoc function + * @name angular.isUndefined + * @module ng + * @kind function + * + * @description + * Determines if a reference is undefined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is undefined. + */ +function isUndefined(value){return typeof value === 'undefined';} + + +/** + * @ngdoc function + * @name angular.isDefined + * @module ng + * @kind function + * + * @description + * Determines if a reference is defined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is defined. + */ +function isDefined(value){return typeof value !== 'undefined';} + + +/** + * @ngdoc function + * @name angular.isObject + * @module ng + * @kind function + * + * @description + * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not + * considered to be objects. Note that JavaScript arrays are objects. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Object` but not `null`. + */ +function isObject(value){return value != null && typeof value === 'object';} + + +/** + * @ngdoc function + * @name angular.isString + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `String`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `String`. + */ +function isString(value){return typeof value === 'string';} + + +/** + * @ngdoc function + * @name angular.isNumber + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `Number`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Number`. + */ +function isNumber(value){return typeof value === 'number';} + + +/** + * @ngdoc function + * @name angular.isDate + * @module ng + * @kind function + * + * @description + * Determines if a value is a date. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Date`. + */ +function isDate(value) { + return toString.call(value) === '[object Date]'; +} + + +/** + * @ngdoc function + * @name angular.isArray + * @module ng + * @kind function + * + * @description + * Determines if a reference is an `Array`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Array`. + */ +var isArray = (function() { + if (!isFunction(Array.isArray)) { + return function(value) { + return toString.call(value) === '[object Array]'; + }; + } + return Array.isArray; +})(); + +/** + * @ngdoc function + * @name angular.isFunction + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `Function`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Function`. + */ +function isFunction(value){return typeof value === 'function';} + + +/** + * Determines if a value is a regular expression object. + * + * @private + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `RegExp`. + */ +function isRegExp(value) { + return toString.call(value) === '[object RegExp]'; +} + + +/** + * Checks if `obj` is a window object. + * + * @private + * @param {*} obj Object to check + * @returns {boolean} True if `obj` is a window obj. + */ +function isWindow(obj) { + return obj && obj.document && obj.location && obj.alert && obj.setInterval; +} + + +function isScope(obj) { + return obj && obj.$evalAsync && obj.$watch; +} + + +function isFile(obj) { + return toString.call(obj) === '[object File]'; +} + + +function isBlob(obj) { + return toString.call(obj) === '[object Blob]'; +} + + +function isBoolean(value) { + return typeof value === 'boolean'; +} + + +function isPromiseLike(obj) { + return obj && isFunction(obj.then); +} + + +var trim = (function() { + // native trim is way faster: http://jsperf.com/angular-trim-test + // but IE doesn't have it... :-( + // TODO: we should move this into IE/ES5 polyfill + if (!String.prototype.trim) { + return function(value) { + return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value; + }; + } + return function(value) { + return isString(value) ? value.trim() : value; + }; +})(); + + +/** + * @ngdoc function + * @name angular.isElement + * @module ng + * @kind function + * + * @description + * Determines if a reference is a DOM element (or wrapped jQuery element). + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). + */ +function isElement(node) { + return !!(node && + (node.nodeName // we are a direct element + || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API +} + +/** + * @param str 'key1,key2,...' + * @returns {object} in the form of {key1:true, key2:true, ...} + */ +function makeMap(str) { + var obj = {}, items = str.split(","), i; + for ( i = 0; i < items.length; i++ ) + obj[ items[i] ] = true; + return obj; +} + + +if (msie < 9) { + nodeName_ = function(element) { + element = element.nodeName ? element : element[0]; + return (element.scopeName && element.scopeName != 'HTML') + ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; + }; +} else { + nodeName_ = function(element) { + return element.nodeName ? element.nodeName : element[0].nodeName; + }; +} + + +function map(obj, iterator, context) { + var results = []; + forEach(obj, function(value, index, list) { + results.push(iterator.call(context, value, index, list)); + }); + return results; +} + + +/** + * @description + * Determines the number of elements in an array, the number of properties an object has, or + * the length of a string. + * + * Note: This function is used to augment the Object type in Angular expressions. See + * {@link angular.Object} for more information about Angular arrays. + * + * @param {Object|Array|string} obj Object, array, or string to inspect. + * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object + * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. + */ +function size(obj, ownPropsOnly) { + var count = 0, key; + + if (isArray(obj) || isString(obj)) { + return obj.length; + } else if (isObject(obj)) { + for (key in obj) + if (!ownPropsOnly || obj.hasOwnProperty(key)) + count++; + } + + return count; +} + + +function includes(array, obj) { + return indexOf(array, obj) != -1; +} + +function indexOf(array, obj) { + if (array.indexOf) return array.indexOf(obj); + + for (var i = 0; i < array.length; i++) { + if (obj === array[i]) return i; + } + return -1; +} + +function arrayRemove(array, value) { + var index = indexOf(array, value); + if (index >=0) + array.splice(index, 1); + return value; +} + +function isLeafNode (node) { + if (node) { + switch (node.nodeName) { + case "OPTION": + case "PRE": + case "TITLE": + return true; + } + } + return false; +} + +/** + * @ngdoc function + * @name angular.copy + * @module ng + * @kind function + * + * @description + * Creates a deep copy of `source`, which should be an object or an array. + * + * * If no destination is supplied, a copy of the object or array is created. + * * If a destination is provided, all of its elements (for array) or properties (for objects) + * are deleted and then all elements/properties from the source are copied to it. + * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. + * * If `source` is identical to 'destination' an exception will be thrown. + * + * @param {*} source The source that will be used to make a copy. + * Can be any type, including primitives, `null`, and `undefined`. + * @param {(Object|Array)=} destination Destination into which the source is copied. If + * provided, must be of the same type as `source`. + * @returns {*} The copy or updated `destination`, if `destination` was specified. + * + * @example + + +
+
+ Name:
+ E-mail:
+ Gender: male + female
+ + +
+
form = {{user | json}}
+
master = {{master | json}}
+
+ + +
+
+ */ +function copy(source, destination, stackSource, stackDest) { + if (isWindow(source) || isScope(source)) { + throw ngMinErr('cpws', + "Can't copy! Making copies of Window or Scope instances is not supported."); + } + + if (!destination) { + destination = source; + if (source) { + if (isArray(source)) { + destination = copy(source, [], stackSource, stackDest); + } else if (isDate(source)) { + destination = new Date(source.getTime()); + } else if (isRegExp(source)) { + destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); + destination.lastIndex = source.lastIndex; + } else if (isObject(source)) { + destination = copy(source, {}, stackSource, stackDest); + } + } + } else { + if (source === destination) throw ngMinErr('cpi', + "Can't copy! Source and destination are identical."); + + stackSource = stackSource || []; + stackDest = stackDest || []; + + if (isObject(source)) { + var index = indexOf(stackSource, source); + if (index !== -1) return stackDest[index]; + + stackSource.push(source); + stackDest.push(destination); + } + + var result; + if (isArray(source)) { + destination.length = 0; + for ( var i = 0; i < source.length; i++) { + result = copy(source[i], null, stackSource, stackDest); + if (isObject(source[i])) { + stackSource.push(source[i]); + stackDest.push(result); + } + destination.push(result); + } + } else { + var h = destination.$$hashKey; + if (isArray(destination)) { + destination.length = 0; + } else { + forEach(destination, function(value, key) { + delete destination[key]; + }); + } + for ( var key in source) { + result = copy(source[key], null, stackSource, stackDest); + if (isObject(source[key])) { + stackSource.push(source[key]); + stackDest.push(result); + } + destination[key] = result; + } + setHashKey(destination,h); + } + + } + return destination; +} + +/** + * Creates a shallow copy of an object, an array or a primitive + */ +function shallowCopy(src, dst) { + if (isArray(src)) { + dst = dst || []; + + for ( var i = 0; i < src.length; i++) { + dst[i] = src[i]; + } + } else if (isObject(src)) { + dst = dst || {}; + + for (var key in src) { + if (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) { + dst[key] = src[key]; + } + } + } + + return dst || src; +} + + +/** + * @ngdoc function + * @name angular.equals + * @module ng + * @kind function + * + * @description + * Determines if two objects or two values are equivalent. Supports value types, regular + * expressions, arrays and objects. + * + * Two objects or values are considered equivalent if at least one of the following is true: + * + * * Both objects or values pass `===` comparison. + * * Both objects or values are of the same type and all of their properties are equal by + * comparing them with `angular.equals`. + * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) + * * Both values represent the same regular expression (In JavaScript, + * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual + * representation matches). + * + * During a property comparison, properties of `function` type and properties with names + * that begin with `$` are ignored. + * + * Scope and DOMWindow objects are being compared only by identify (`===`). + * + * @param {*} o1 Object or value to compare. + * @param {*} o2 Object or value to compare. + * @returns {boolean} True if arguments are equal. + */ +function equals(o1, o2) { + if (o1 === o2) return true; + if (o1 === null || o2 === null) return false; + if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN + var t1 = typeof o1, t2 = typeof o2, length, key, keySet; + if (t1 == t2) { + if (t1 == 'object') { + if (isArray(o1)) { + if (!isArray(o2)) return false; + if ((length = o1.length) == o2.length) { + for(key=0; key 2 ? sliceArgs(arguments, 2) : []; + if (isFunction(fn) && !(fn instanceof RegExp)) { + return curryArgs.length + ? function() { + return arguments.length + ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) + : fn.apply(self, curryArgs); + } + : function() { + return arguments.length + ? fn.apply(self, arguments) + : fn.call(self); + }; + } else { + // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) + return fn; + } +} + + +function toJsonReplacer(key, value) { + var val = value; + + if (typeof key === 'string' && key.charAt(0) === '$') { + val = undefined; + } else if (isWindow(value)) { + val = '$WINDOW'; + } else if (value && document === value) { + val = '$DOCUMENT'; + } else if (isScope(value)) { + val = '$SCOPE'; + } + + return val; +} + + +/** + * @ngdoc function + * @name angular.toJson + * @module ng + * @kind function + * + * @description + * Serializes input into a JSON-formatted string. Properties with leading $ characters will be + * stripped since angular uses this notation internally. + * + * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. + * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace. + * @returns {string|undefined} JSON-ified string representing `obj`. + */ +function toJson(obj, pretty) { + if (typeof obj === 'undefined') return undefined; + return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); +} + + +/** + * @ngdoc function + * @name angular.fromJson + * @module ng + * @kind function + * + * @description + * Deserializes a JSON string. + * + * @param {string} json JSON string to deserialize. + * @returns {Object|Array|string|number} Deserialized thingy. + */ +function fromJson(json) { + return isString(json) + ? JSON.parse(json) + : json; +} + + +function toBoolean(value) { + if (typeof value === 'function') { + value = true; + } else if (value && value.length !== 0) { + var v = lowercase("" + value); + value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]'); + } else { + value = false; + } + return value; +} + +/** + * @returns {string} Returns the string representation of the element. + */ +function startingTag(element) { + element = jqLite(element).clone(); + try { + // turns out IE does not let you set .html() on elements which + // are not allowed to have children. So we just ignore it. + element.empty(); + } catch(e) {} + // As Per DOM Standards + var TEXT_NODE = 3; + var elemHtml = jqLite('
').append(element).html(); + try { + return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) : + elemHtml. + match(/^(<[^>]+>)/)[1]. + replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); + } catch(e) { + return lowercase(elemHtml); + } + +} + + +///////////////////////////////////////////////// + +/** + * Tries to decode the URI component without throwing an exception. + * + * @private + * @param str value potential URI component to check. + * @returns {boolean} True if `value` can be decoded + * with the decodeURIComponent function. + */ +function tryDecodeURIComponent(value) { + try { + return decodeURIComponent(value); + } catch(e) { + // Ignore any invalid uri component + } +} + + +/** + * Parses an escaped url query string into key-value pairs. + * @returns {Object.} + */ +function parseKeyValue(/**string*/keyValue) { + var obj = {}, key_value, key; + forEach((keyValue || "").split('&'), function(keyValue) { + if ( keyValue ) { + key_value = keyValue.replace(/\+/g,'%20').split('='); + key = tryDecodeURIComponent(key_value[0]); + if ( isDefined(key) ) { + var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; + if (!hasOwnProperty.call(obj, key)) { + obj[key] = val; + } else if(isArray(obj[key])) { + obj[key].push(val); + } else { + obj[key] = [obj[key],val]; + } + } + } + }); + return obj; +} + +function toKeyValue(obj) { + var parts = []; + forEach(obj, function(value, key) { + if (isArray(value)) { + forEach(value, function(arrayValue) { + parts.push(encodeUriQuery(key, true) + + (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); + }); + } else { + parts.push(encodeUriQuery(key, true) + + (value === true ? '' : '=' + encodeUriQuery(value, true))); + } + }); + return parts.length ? parts.join('&') : ''; +} + + +/** + * We need our custom method because encodeURIComponent is too aggressive and doesn't follow + * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path + * segments: + * segment = *pchar + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * pct-encoded = "%" HEXDIG HEXDIG + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriSegment(val) { + return encodeUriQuery(val, true). + replace(/%26/gi, '&'). + replace(/%3D/gi, '='). + replace(/%2B/gi, '+'); +} + + +/** + * This method is intended for encoding *key* or *value* parts of query component. We need a custom + * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be + * encoded per http://tools.ietf.org/html/rfc3986: + * query = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriQuery(val, pctEncodeSpaces) { + return encodeURIComponent(val). + replace(/%40/gi, '@'). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); +} + + +/** + * @ngdoc directive + * @name ngApp + * @module ng + * + * @element ANY + * @param {angular.Module} ngApp an optional application + * {@link angular.module module} name to load. + * + * @description + * + * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive + * designates the **root element** of the application and is typically placed near the root element + * of the page - e.g. on the `` or `` tags. + * + * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` + * found in the document will be used to define the root element to auto-bootstrap as an + * application. To run multiple applications in an HTML document you must manually bootstrap them using + * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. + * + * You can specify an **AngularJS module** to be used as the root module for the application. This + * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and + * should contain the application code needed or have dependencies on other modules that will + * contain the code. See {@link angular.module} for more information. + * + * In the example below if the `ngApp` directive were not placed on the `html` element then the + * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` + * would not be resolved to `3`. + * + * `ngApp` is the easiest, and most common, way to bootstrap an application. + * + + +
+ I can add: {{a}} + {{b}} = {{ a+b }} +
+
+ + angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { + $scope.a = 1; + $scope.b = 2; + }); + +
+ * + */ +function angularInit(element, bootstrap) { + var elements = [element], + appElement, + module, + names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], + NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/; + + function append(element) { + element && elements.push(element); + } + + forEach(names, function(name) { + names[name] = true; + append(document.getElementById(name)); + name = name.replace(':', '\\:'); + if (element.querySelectorAll) { + forEach(element.querySelectorAll('.' + name), append); + forEach(element.querySelectorAll('.' + name + '\\:'), append); + forEach(element.querySelectorAll('[' + name + ']'), append); + } + }); + + forEach(elements, function(element) { + if (!appElement) { + var className = ' ' + element.className + ' '; + var match = NG_APP_CLASS_REGEXP.exec(className); + if (match) { + appElement = element; + module = (match[2] || '').replace(/\s+/g, ','); + } else { + forEach(element.attributes, function(attr) { + if (!appElement && names[attr.name]) { + appElement = element; + module = attr.value; + } + }); + } + } + }); + if (appElement) { + bootstrap(appElement, module ? [module] : []); + } +} + +/** + * @ngdoc function + * @name angular.bootstrap + * @module ng + * @description + * Use this function to manually start up angular application. + * + * See: {@link guide/bootstrap Bootstrap} + * + * Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually. + * They must use {@link ng.directive:ngApp ngApp}. + * + * Angular will detect if it has been loaded into the browser more than once and only allow the + * first loaded script to be bootstrapped and will report a warning to the browser console for + * each of the subsequent scripts. This prevents strange results in applications, where otherwise + * multiple instances of Angular try to work on the DOM. + * + * + * + * + *
+ * + * + * + * + * + * + * + *
{{heading}}
{{fill}}
+ *
+ *
+ * + * var app = angular.module('multi-bootstrap', []) + * + * .controller('BrokenTable', function($scope) { + * $scope.headings = ['One', 'Two', 'Three']; + * $scope.fillings = [[1, 2, 3], ['A', 'B', 'C'], [7, 8, 9]]; + * }); + * + * + * it('should only insert one table cell for each item in $scope.fillings', function() { + * expect(element.all(by.css('td')).count()) + * .toBe(9); + * }); + * + *
+ * + * @param {DOMElement} element DOM element which is the root of angular application. + * @param {Array=} modules an array of modules to load into the application. + * Each item in the array should be the name of a predefined module or a (DI annotated) + * function that will be invoked by the injector as a run block. + * See: {@link angular.module modules} + * @returns {auto.$injector} Returns the newly created injector for this app. + */ +function bootstrap(element, modules) { + var doBootstrap = function() { + element = jqLite(element); + + if (element.injector()) { + var tag = (element[0] === document) ? 'document' : startingTag(element); + //Encode angle brackets to prevent input from being sanitized to empty string #8683 + throw ngMinErr( + 'btstrpd', + "App Already Bootstrapped with this Element '{0}'", + tag.replace(//,'>')); + } + + modules = modules || []; + modules.unshift(['$provide', function($provide) { + $provide.value('$rootElement', element); + }]); + modules.unshift('ng'); + var injector = createInjector(modules); + injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate', + function(scope, element, compile, injector, animate) { + scope.$apply(function() { + element.data('$injector', injector); + compile(element)(scope); + }); + }] + ); + return injector; + }; + + var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; + + if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { + return doBootstrap(); + } + + window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); + angular.resumeBootstrap = function(extraModules) { + forEach(extraModules, function(module) { + modules.push(module); + }); + doBootstrap(); + }; +} + +var SNAKE_CASE_REGEXP = /[A-Z]/g; +function snake_case(name, separator) { + separator = separator || '_'; + return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); +} + +function bindJQuery() { + // bind to jQuery if present; + jQuery = window.jQuery; + // Use jQuery if it exists with proper functionality, otherwise default to us. + // Angular 1.2+ requires jQuery 1.7.1+ for on()/off() support. + if (jQuery && jQuery.fn.on) { + jqLite = jQuery; + extend(jQuery.fn, { + scope: JQLitePrototype.scope, + isolateScope: JQLitePrototype.isolateScope, + controller: JQLitePrototype.controller, + injector: JQLitePrototype.injector, + inheritedData: JQLitePrototype.inheritedData + }); + // Method signature: + // jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) + jqLitePatchJQueryRemove('remove', true, true, false); + jqLitePatchJQueryRemove('empty', false, false, false); + jqLitePatchJQueryRemove('html', false, false, true); + } else { + jqLite = JQLite; + } + angular.element = jqLite; +} + +/** + * throw error if the argument is falsy. + */ +function assertArg(arg, name, reason) { + if (!arg) { + throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); + } + return arg; +} + +function assertArgFn(arg, name, acceptArrayAnnotation) { + if (acceptArrayAnnotation && isArray(arg)) { + arg = arg[arg.length - 1]; + } + + assertArg(isFunction(arg), name, 'not a function, got ' + + (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); + return arg; +} + +/** + * throw error if the name given is hasOwnProperty + * @param {String} name the name to test + * @param {String} context the context in which the name is used, such as module or directive + */ +function assertNotHasOwnProperty(name, context) { + if (name === 'hasOwnProperty') { + throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context); + } +} + +/** + * Return the value accessible from the object by path. Any undefined traversals are ignored + * @param {Object} obj starting object + * @param {String} path path to traverse + * @param {boolean} [bindFnToScope=true] + * @returns {Object} value as accessible by path + */ +//TODO(misko): this function needs to be removed +function getter(obj, path, bindFnToScope) { + if (!path) return obj; + var keys = path.split('.'); + var key; + var lastInstance = obj; + var len = keys.length; + + for (var i = 0; i < len; i++) { + key = keys[i]; + if (obj) { + obj = (lastInstance = obj)[key]; + } + } + if (!bindFnToScope && isFunction(obj)) { + return bind(lastInstance, obj); + } + return obj; +} + +/** + * Return the DOM siblings between the first and last node in the given array. + * @param {Array} array like object + * @returns {DOMElement} object containing the elements + */ +function getBlockElements(nodes) { + var startNode = nodes[0], + endNode = nodes[nodes.length - 1]; + if (startNode === endNode) { + return jqLite(startNode); + } + + var element = startNode; + var elements = [element]; + + do { + element = element.nextSibling; + if (!element) break; + elements.push(element); + } while (element !== endNode); + + return jqLite(elements); +} + +/** + * @ngdoc type + * @name angular.Module + * @module ng + * @description + * + * Interface for configuring angular {@link angular.module modules}. + */ + +function setupModuleLoader(window) { + + var $injectorMinErr = minErr('$injector'); + var ngMinErr = minErr('ng'); + + function ensure(obj, name, factory) { + return obj[name] || (obj[name] = factory()); + } + + var angular = ensure(window, 'angular', Object); + + // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap + angular.$$minErr = angular.$$minErr || minErr; + + return ensure(angular, 'module', function() { + /** @type {Object.} */ + var modules = {}; + + /** + * @ngdoc function + * @name angular.module + * @module ng + * @description + * + * The `angular.module` is a global place for creating, registering and retrieving Angular + * modules. + * All modules (angular core or 3rd party) that should be available to an application must be + * registered using this mechanism. + * + * When passed two or more arguments, a new module is created. If passed only one argument, an + * existing module (the name passed as the first argument to `module`) is retrieved. + * + * + * # Module + * + * A module is a collection of services, directives, controllers, filters, and configuration information. + * `angular.module` is used to configure the {@link auto.$injector $injector}. + * + * ```js + * // Create a new module + * var myModule = angular.module('myModule', []); + * + * // register a new service + * myModule.value('appName', 'MyCoolApp'); + * + * // configure existing services inside initialization blocks. + * myModule.config(['$locationProvider', function($locationProvider) { + * // Configure existing providers + * $locationProvider.hashPrefix('!'); + * }]); + * ``` + * + * Then you can create an injector and load your modules like this: + * + * ```js + * var injector = angular.injector(['ng', 'myModule']) + * ``` + * + * However it's more likely that you'll just use + * {@link ng.directive:ngApp ngApp} or + * {@link angular.bootstrap} to simplify this process for you. + * + * @param {!string} name The name of the module to create or retrieve. + * @param {!Array.=} requires If specified then new module is being created. If + * unspecified then the module is being retrieved for further configuration. + * @param {Function=} configFn Optional configuration function for the module. Same as + * {@link angular.Module#config Module#config()}. + * @returns {module} new module with the {@link angular.Module} api. + */ + return function module(name, requires, configFn) { + var assertNotHasOwnProperty = function(name, context) { + if (name === 'hasOwnProperty') { + throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); + } + }; + + assertNotHasOwnProperty(name, 'module'); + if (requires && modules.hasOwnProperty(name)) { + modules[name] = null; + } + return ensure(modules, name, function() { + if (!requires) { + throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + + "the module name or forgot to load it. If registering a module ensure that you " + + "specify the dependencies as the second argument.", name); + } + + /** @type {!Array.>} */ + var invokeQueue = []; + + /** @type {!Array.} */ + var runBlocks = []; + + var config = invokeLater('$injector', 'invoke'); + + /** @type {angular.Module} */ + var moduleInstance = { + // Private state + _invokeQueue: invokeQueue, + _runBlocks: runBlocks, + + /** + * @ngdoc property + * @name angular.Module#requires + * @module ng + * + * @description + * Holds the list of modules which the injector will load before the current module is + * loaded. + */ + requires: requires, + + /** + * @ngdoc property + * @name angular.Module#name + * @module ng + * + * @description + * Name of the module. + */ + name: name, + + + /** + * @ngdoc method + * @name angular.Module#provider + * @module ng + * @param {string} name service name + * @param {Function} providerType Construction function for creating new instance of the + * service. + * @description + * See {@link auto.$provide#provider $provide.provider()}. + */ + provider: invokeLater('$provide', 'provider'), + + /** + * @ngdoc method + * @name angular.Module#factory + * @module ng + * @param {string} name service name + * @param {Function} providerFunction Function for creating new instance of the service. + * @description + * See {@link auto.$provide#factory $provide.factory()}. + */ + factory: invokeLater('$provide', 'factory'), + + /** + * @ngdoc method + * @name angular.Module#service + * @module ng + * @param {string} name service name + * @param {Function} constructor A constructor function that will be instantiated. + * @description + * See {@link auto.$provide#service $provide.service()}. + */ + service: invokeLater('$provide', 'service'), + + /** + * @ngdoc method + * @name angular.Module#value + * @module ng + * @param {string} name service name + * @param {*} object Service instance object. + * @description + * See {@link auto.$provide#value $provide.value()}. + */ + value: invokeLater('$provide', 'value'), + + /** + * @ngdoc method + * @name angular.Module#constant + * @module ng + * @param {string} name constant name + * @param {*} object Constant value. + * @description + * Because the constant are fixed, they get applied before other provide methods. + * See {@link auto.$provide#constant $provide.constant()}. + */ + constant: invokeLater('$provide', 'constant', 'unshift'), + + /** + * @ngdoc method + * @name angular.Module#animation + * @module ng + * @param {string} name animation name + * @param {Function} animationFactory Factory function for creating new instance of an + * animation. + * @description + * + * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. + * + * + * Defines an animation hook that can be later used with + * {@link ngAnimate.$animate $animate} service and directives that use this service. + * + * ```js + * module.animation('.animation-name', function($inject1, $inject2) { + * return { + * eventName : function(element, done) { + * //code to run the animation + * //once complete, then run done() + * return function cancellationFunction(element) { + * //code to cancel the animation + * } + * } + * } + * }) + * ``` + * + * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and + * {@link ngAnimate ngAnimate module} for more information. + */ + animation: invokeLater('$animateProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#filter + * @module ng + * @param {string} name Filter name. + * @param {Function} filterFactory Factory function for creating new instance of filter. + * @description + * See {@link ng.$filterProvider#register $filterProvider.register()}. + */ + filter: invokeLater('$filterProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#controller + * @module ng + * @param {string|Object} name Controller name, or an object map of controllers where the + * keys are the names and the values are the constructors. + * @param {Function} constructor Controller constructor function. + * @description + * See {@link ng.$controllerProvider#register $controllerProvider.register()}. + */ + controller: invokeLater('$controllerProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#directive + * @module ng + * @param {string|Object} name Directive name, or an object map of directives where the + * keys are the names and the values are the factories. + * @param {Function} directiveFactory Factory function for creating new instance of + * directives. + * @description + * See {@link ng.$compileProvider#directive $compileProvider.directive()}. + */ + directive: invokeLater('$compileProvider', 'directive'), + + /** + * @ngdoc method + * @name angular.Module#config + * @module ng + * @param {Function} configFn Execute this function on module load. Useful for service + * configuration. + * @description + * Use this method to register work which needs to be performed on module loading. + * For more about how to configure services, see + * {@link providers#providers_provider-recipe Provider Recipe}. + */ + config: config, + + /** + * @ngdoc method + * @name angular.Module#run + * @module ng + * @param {Function} initializationFn Execute this function after injector creation. + * Useful for application initialization. + * @description + * Use this method to register work which should be performed when the injector is done + * loading all modules. + */ + run: function(block) { + runBlocks.push(block); + return this; + } + }; + + if (configFn) { + config(configFn); + } + + return moduleInstance; + + /** + * @param {string} provider + * @param {string} method + * @param {String=} insertMethod + * @returns {angular.Module} + */ + function invokeLater(provider, method, insertMethod) { + return function() { + invokeQueue[insertMethod || 'push']([provider, method, arguments]); + return moduleInstance; + }; + } + }); + }; + }); + +} + +/* global angularModule: true, + version: true, + + $LocaleProvider, + $CompileProvider, + + htmlAnchorDirective, + inputDirective, + inputDirective, + formDirective, + scriptDirective, + selectDirective, + styleDirective, + optionDirective, + ngBindDirective, + ngBindHtmlDirective, + ngBindTemplateDirective, + ngClassDirective, + ngClassEvenDirective, + ngClassOddDirective, + ngCspDirective, + ngCloakDirective, + ngControllerDirective, + ngFormDirective, + ngHideDirective, + ngIfDirective, + ngIncludeDirective, + ngIncludeFillContentDirective, + ngInitDirective, + ngNonBindableDirective, + ngPluralizeDirective, + ngRepeatDirective, + ngShowDirective, + ngStyleDirective, + ngSwitchDirective, + ngSwitchWhenDirective, + ngSwitchDefaultDirective, + ngOptionsDirective, + ngTranscludeDirective, + ngModelDirective, + ngListDirective, + ngChangeDirective, + requiredDirective, + requiredDirective, + ngValueDirective, + ngAttributeAliasDirectives, + ngEventDirectives, + + $AnchorScrollProvider, + $AnimateProvider, + $BrowserProvider, + $CacheFactoryProvider, + $ControllerProvider, + $DocumentProvider, + $ExceptionHandlerProvider, + $FilterProvider, + $InterpolateProvider, + $IntervalProvider, + $HttpProvider, + $HttpBackendProvider, + $LocationProvider, + $LogProvider, + $ParseProvider, + $RootScopeProvider, + $QProvider, + $$SanitizeUriProvider, + $SceProvider, + $SceDelegateProvider, + $SnifferProvider, + $TemplateCacheProvider, + $TimeoutProvider, + $$RAFProvider, + $$AsyncCallbackProvider, + $WindowProvider +*/ + + +/** + * @ngdoc object + * @name angular.version + * @module ng + * @description + * An object that contains information about the current AngularJS version. This object has the + * following properties: + * + * - `full` – `{string}` – Full version string, such as "0.9.18". + * - `major` – `{number}` – Major version number, such as "0". + * - `minor` – `{number}` – Minor version number, such as "9". + * - `dot` – `{number}` – Dot version number, such as "18". + * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". + */ +var version = { + full: '1.2.25', // all of these placeholder strings will be replaced by grunt's + major: 1, // package task + minor: 2, + dot: 25, + codeName: 'hypnotic-gesticulation' +}; + + +function publishExternalAPI(angular){ + extend(angular, { + 'bootstrap': bootstrap, + 'copy': copy, + 'extend': extend, + 'equals': equals, + 'element': jqLite, + 'forEach': forEach, + 'injector': createInjector, + 'noop': noop, + 'bind': bind, + 'toJson': toJson, + 'fromJson': fromJson, + 'identity': identity, + 'isUndefined': isUndefined, + 'isDefined': isDefined, + 'isString': isString, + 'isFunction': isFunction, + 'isObject': isObject, + 'isNumber': isNumber, + 'isElement': isElement, + 'isArray': isArray, + 'version': version, + 'isDate': isDate, + 'lowercase': lowercase, + 'uppercase': uppercase, + 'callbacks': {counter: 0}, + '$$minErr': minErr, + '$$csp': csp + }); + + angularModule = setupModuleLoader(window); + try { + angularModule('ngLocale'); + } catch (e) { + angularModule('ngLocale', []).provider('$locale', $LocaleProvider); + } + + angularModule('ng', ['ngLocale'], ['$provide', + function ngModule($provide) { + // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. + $provide.provider({ + $$sanitizeUri: $$SanitizeUriProvider + }); + $provide.provider('$compile', $CompileProvider). + directive({ + a: htmlAnchorDirective, + input: inputDirective, + textarea: inputDirective, + form: formDirective, + script: scriptDirective, + select: selectDirective, + style: styleDirective, + option: optionDirective, + ngBind: ngBindDirective, + ngBindHtml: ngBindHtmlDirective, + ngBindTemplate: ngBindTemplateDirective, + ngClass: ngClassDirective, + ngClassEven: ngClassEvenDirective, + ngClassOdd: ngClassOddDirective, + ngCloak: ngCloakDirective, + ngController: ngControllerDirective, + ngForm: ngFormDirective, + ngHide: ngHideDirective, + ngIf: ngIfDirective, + ngInclude: ngIncludeDirective, + ngInit: ngInitDirective, + ngNonBindable: ngNonBindableDirective, + ngPluralize: ngPluralizeDirective, + ngRepeat: ngRepeatDirective, + ngShow: ngShowDirective, + ngStyle: ngStyleDirective, + ngSwitch: ngSwitchDirective, + ngSwitchWhen: ngSwitchWhenDirective, + ngSwitchDefault: ngSwitchDefaultDirective, + ngOptions: ngOptionsDirective, + ngTransclude: ngTranscludeDirective, + ngModel: ngModelDirective, + ngList: ngListDirective, + ngChange: ngChangeDirective, + required: requiredDirective, + ngRequired: requiredDirective, + ngValue: ngValueDirective + }). + directive({ + ngInclude: ngIncludeFillContentDirective + }). + directive(ngAttributeAliasDirectives). + directive(ngEventDirectives); + $provide.provider({ + $anchorScroll: $AnchorScrollProvider, + $animate: $AnimateProvider, + $browser: $BrowserProvider, + $cacheFactory: $CacheFactoryProvider, + $controller: $ControllerProvider, + $document: $DocumentProvider, + $exceptionHandler: $ExceptionHandlerProvider, + $filter: $FilterProvider, + $interpolate: $InterpolateProvider, + $interval: $IntervalProvider, + $http: $HttpProvider, + $httpBackend: $HttpBackendProvider, + $location: $LocationProvider, + $log: $LogProvider, + $parse: $ParseProvider, + $rootScope: $RootScopeProvider, + $q: $QProvider, + $sce: $SceProvider, + $sceDelegate: $SceDelegateProvider, + $sniffer: $SnifferProvider, + $templateCache: $TemplateCacheProvider, + $timeout: $TimeoutProvider, + $window: $WindowProvider, + $$rAF: $$RAFProvider, + $$asyncCallback : $$AsyncCallbackProvider + }); + } + ]); +} + +/* global JQLitePrototype: true, + addEventListenerFn: true, + removeEventListenerFn: true, + BOOLEAN_ATTR: true +*/ + +////////////////////////////////// +//JQLite +////////////////////////////////// + +/** + * @ngdoc function + * @name angular.element + * @module ng + * @kind function + * + * @description + * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. + * + * If jQuery is available, `angular.element` is an alias for the + * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` + * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite." + * + *
jqLite is a tiny, API-compatible subset of jQuery that allows + * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most + * commonly needed functionality with the goal of having a very small footprint.
+ * + * To use jQuery, simply load it before `DOMContentLoaded` event fired. + * + *
**Note:** all element references in Angular are always wrapped with jQuery or + * jqLite; they are never raw DOM references.
+ * + * ## Angular's jqLite + * jqLite provides only the following jQuery methods: + * + * - [`addClass()`](http://api.jquery.com/addClass/) + * - [`after()`](http://api.jquery.com/after/) + * - [`append()`](http://api.jquery.com/append/) + * - [`attr()`](http://api.jquery.com/attr/) + * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData + * - [`children()`](http://api.jquery.com/children/) - Does not support selectors + * - [`clone()`](http://api.jquery.com/clone/) + * - [`contents()`](http://api.jquery.com/contents/) + * - [`css()`](http://api.jquery.com/css/) + * - [`data()`](http://api.jquery.com/data/) + * - [`empty()`](http://api.jquery.com/empty/) + * - [`eq()`](http://api.jquery.com/eq/) + * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name + * - [`hasClass()`](http://api.jquery.com/hasClass/) + * - [`html()`](http://api.jquery.com/html/) + * - [`next()`](http://api.jquery.com/next/) - Does not support selectors + * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData + * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors + * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors + * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors + * - [`prepend()`](http://api.jquery.com/prepend/) + * - [`prop()`](http://api.jquery.com/prop/) + * - [`ready()`](http://api.jquery.com/ready/) + * - [`remove()`](http://api.jquery.com/remove/) + * - [`removeAttr()`](http://api.jquery.com/removeAttr/) + * - [`removeClass()`](http://api.jquery.com/removeClass/) + * - [`removeData()`](http://api.jquery.com/removeData/) + * - [`replaceWith()`](http://api.jquery.com/replaceWith/) + * - [`text()`](http://api.jquery.com/text/) + * - [`toggleClass()`](http://api.jquery.com/toggleClass/) + * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. + * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces + * - [`val()`](http://api.jquery.com/val/) + * - [`wrap()`](http://api.jquery.com/wrap/) + * + * ## jQuery/jqLite Extras + * Angular also provides the following additional methods and events to both jQuery and jqLite: + * + * ### Events + * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event + * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM + * element before it is removed. + * + * ### Methods + * - `controller(name)` - retrieves the controller of the current element or its parent. By default + * retrieves controller associated with the `ngController` directive. If `name` is provided as + * camelCase directive name, then the controller for this directive will be retrieved (e.g. + * `'ngModel'`). + * - `injector()` - retrieves the injector of the current element or its parent. + * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current + * element or its parent. + * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the + * current element. This getter should be used only on elements that contain a directive which starts a new isolate + * scope. Calling `scope()` on this element always returns the original non-isolate scope. + * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top + * parent element is reached. + * + * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. + * @returns {Object} jQuery object. + */ + +JQLite.expando = 'ng339'; + +var jqCache = JQLite.cache = {}, + jqId = 1, + addEventListenerFn = (window.document.addEventListener + ? function(element, type, fn) {element.addEventListener(type, fn, false);} + : function(element, type, fn) {element.attachEvent('on' + type, fn);}), + removeEventListenerFn = (window.document.removeEventListener + ? function(element, type, fn) {element.removeEventListener(type, fn, false); } + : function(element, type, fn) {element.detachEvent('on' + type, fn); }); + +/* + * !!! This is an undocumented "private" function !!! + */ +var jqData = JQLite._data = function(node) { + //jQuery always returns an object on cache miss + return this.cache[node[this.expando]] || {}; +}; + +function jqNextId() { return ++jqId; } + + +var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; +var MOZ_HACK_REGEXP = /^moz([A-Z])/; +var jqLiteMinErr = minErr('jqLite'); + +/** + * Converts snake_case to camelCase. + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ +function camelCase(name) { + return name. + replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }). + replace(MOZ_HACK_REGEXP, 'Moz$1'); +} + +///////////////////////////////////////////// +// jQuery mutation patch +// +// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a +// $destroy event on all DOM nodes being removed. +// +///////////////////////////////////////////// + +function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) { + var originalJqFn = jQuery.fn[name]; + originalJqFn = originalJqFn.$original || originalJqFn; + removePatch.$original = originalJqFn; + jQuery.fn[name] = removePatch; + + function removePatch(param) { + // jshint -W040 + var list = filterElems && param ? [this.filter(param)] : [this], + fireEvent = dispatchThis, + set, setIndex, setLength, + element, childIndex, childLength, children; + + if (!getterIfNoArguments || param != null) { + while(list.length) { + set = list.shift(); + for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) { + element = jqLite(set[setIndex]); + if (fireEvent) { + element.triggerHandler('$destroy'); + } else { + fireEvent = !fireEvent; + } + for(childIndex = 0, childLength = (children = element.children()).length; + childIndex < childLength; + childIndex++) { + list.push(jQuery(children[childIndex])); + } + } + } + } + return originalJqFn.apply(this, arguments); + } +} + +var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/; +var HTML_REGEXP = /<|&#?\w+;/; +var TAG_NAME_REGEXP = /<([\w:]+)/; +var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi; + +var wrapMap = { + 'option': [1, ''], + + 'thead': [1, '', '
'], + 'col': [2, '', '
'], + 'tr': [2, '', '
'], + 'td': [3, '', '
'], + '_default': [0, "", ""] +}; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +function jqLiteIsTextNode(html) { + return !HTML_REGEXP.test(html); +} + +function jqLiteBuildFragment(html, context) { + var elem, tmp, tag, wrap, + fragment = context.createDocumentFragment(), + nodes = [], i, j, jj; + + if (jqLiteIsTextNode(html)) { + // Convert non-html into a text node + nodes.push(context.createTextNode(html)); + } else { + tmp = fragment.appendChild(context.createElement('div')); + // Convert html into DOM nodes + tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase(); + wrap = wrapMap[tag] || wrapMap._default; + tmp.innerHTML = '
 
' + + wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1>") + wrap[2]; + tmp.removeChild(tmp.firstChild); + + // Descend through wrappers to the right content + i = wrap[0]; + while (i--) { + tmp = tmp.lastChild; + } + + for (j=0, jj=tmp.childNodes.length; j -1); +} + +function jqLiteRemoveClass(element, cssClasses) { + if (cssClasses && element.setAttribute) { + forEach(cssClasses.split(' '), function(cssClass) { + element.setAttribute('class', trim( + (" " + (element.getAttribute('class') || '') + " ") + .replace(/[\n\t]/g, " ") + .replace(" " + trim(cssClass) + " ", " ")) + ); + }); + } +} + +function jqLiteAddClass(element, cssClasses) { + if (cssClasses && element.setAttribute) { + var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') + .replace(/[\n\t]/g, " "); + + forEach(cssClasses.split(' '), function(cssClass) { + cssClass = trim(cssClass); + if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { + existingClasses += cssClass + ' '; + } + }); + + element.setAttribute('class', trim(existingClasses)); + } +} + +function jqLiteAddNodes(root, elements) { + if (elements) { + elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) + ? elements + : [ elements ]; + for(var i=0; i < elements.length; i++) { + root.push(elements[i]); + } + } +} + +function jqLiteController(element, name) { + return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); +} + +function jqLiteInheritedData(element, name, value) { + // if element is the document object work with the html element instead + // this makes $(document).scope() possible + if(element.nodeType == 9) { + element = element.documentElement; + } + var names = isArray(name) ? name : [name]; + + while (element) { + for (var i = 0, ii = names.length; i < ii; i++) { + if ((value = jqLite.data(element, names[i])) !== undefined) return value; + } + + // If dealing with a document fragment node with a host element, and no parent, use the host + // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM + // to lookup parent controllers. + element = element.parentNode || (element.nodeType === 11 && element.host); + } +} + +function jqLiteEmpty(element) { + for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { + jqLiteDealoc(childNodes[i]); + } + while (element.firstChild) { + element.removeChild(element.firstChild); + } +} + +////////////////////////////////////////// +// Functions which are declared directly. +////////////////////////////////////////// +var JQLitePrototype = JQLite.prototype = { + ready: function(fn) { + var fired = false; + + function trigger() { + if (fired) return; + fired = true; + fn(); + } + + // check if document already is loaded + if (document.readyState === 'complete'){ + setTimeout(trigger); + } else { + this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 + // we can not use jqLite since we are not done loading and jQuery could be loaded later. + // jshint -W064 + JQLite(window).on('load', trigger); // fallback to window.onload for others + // jshint +W064 + } + }, + toString: function() { + var value = []; + forEach(this, function(e){ value.push('' + e);}); + return '[' + value.join(', ') + ']'; + }, + + eq: function(index) { + return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); + }, + + length: 0, + push: push, + sort: [].sort, + splice: [].splice +}; + +////////////////////////////////////////// +// Functions iterating getter/setters. +// these functions return self on setter and +// value on get. +////////////////////////////////////////// +var BOOLEAN_ATTR = {}; +forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { + BOOLEAN_ATTR[lowercase(value)] = value; +}); +var BOOLEAN_ELEMENTS = {}; +forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { + BOOLEAN_ELEMENTS[uppercase(value)] = true; +}); + +function getBooleanAttrName(element, name) { + // check dom last since we will most likely fail on name + var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; + + // booleanAttr is here twice to minimize DOM access + return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; +} + +forEach({ + data: jqLiteData, + removeData: jqLiteRemoveData +}, function(fn, name) { + JQLite[name] = fn; +}); + +forEach({ + data: jqLiteData, + inheritedData: jqLiteInheritedData, + + scope: function(element) { + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); + }, + + isolateScope: function(element) { + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); + }, + + controller: jqLiteController, + + injector: function(element) { + return jqLiteInheritedData(element, '$injector'); + }, + + removeAttr: function(element,name) { + element.removeAttribute(name); + }, + + hasClass: jqLiteHasClass, + + css: function(element, name, value) { + name = camelCase(name); + + if (isDefined(value)) { + element.style[name] = value; + } else { + var val; + + if (msie <= 8) { + // this is some IE specific weirdness that jQuery 1.6.4 does not sure why + val = element.currentStyle && element.currentStyle[name]; + if (val === '') val = 'auto'; + } + + val = val || element.style[name]; + + if (msie <= 8) { + // jquery weirdness :-/ + val = (val === '') ? undefined : val; + } + + return val; + } + }, + + attr: function(element, name, value){ + var lowercasedName = lowercase(name); + if (BOOLEAN_ATTR[lowercasedName]) { + if (isDefined(value)) { + if (!!value) { + element[name] = true; + element.setAttribute(name, lowercasedName); + } else { + element[name] = false; + element.removeAttribute(lowercasedName); + } + } else { + return (element[name] || + (element.attributes.getNamedItem(name)|| noop).specified) + ? lowercasedName + : undefined; + } + } else if (isDefined(value)) { + element.setAttribute(name, value); + } else if (element.getAttribute) { + // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code + // some elements (e.g. Document) don't have get attribute, so return undefined + var ret = element.getAttribute(name, 2); + // normalize non-existing attributes to undefined (as jQuery) + return ret === null ? undefined : ret; + } + }, + + prop: function(element, name, value) { + if (isDefined(value)) { + element[name] = value; + } else { + return element[name]; + } + }, + + text: (function() { + var NODE_TYPE_TEXT_PROPERTY = []; + if (msie < 9) { + NODE_TYPE_TEXT_PROPERTY[1] = 'innerText'; /** Element **/ + NODE_TYPE_TEXT_PROPERTY[3] = 'nodeValue'; /** Text **/ + } else { + NODE_TYPE_TEXT_PROPERTY[1] = /** Element **/ + NODE_TYPE_TEXT_PROPERTY[3] = 'textContent'; /** Text **/ + } + getText.$dv = ''; + return getText; + + function getText(element, value) { + var textProp = NODE_TYPE_TEXT_PROPERTY[element.nodeType]; + if (isUndefined(value)) { + return textProp ? element[textProp] : ''; + } + element[textProp] = value; + } + })(), + + val: function(element, value) { + if (isUndefined(value)) { + if (nodeName_(element) === 'SELECT' && element.multiple) { + var result = []; + forEach(element.options, function (option) { + if (option.selected) { + result.push(option.value || option.text); + } + }); + return result.length === 0 ? null : result; + } + return element.value; + } + element.value = value; + }, + + html: function(element, value) { + if (isUndefined(value)) { + return element.innerHTML; + } + for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { + jqLiteDealoc(childNodes[i]); + } + element.innerHTML = value; + }, + + empty: jqLiteEmpty +}, function(fn, name){ + /** + * Properties: writes return selection, reads return first value + */ + JQLite.prototype[name] = function(arg1, arg2) { + var i, key; + var nodeCount = this.length; + + // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it + // in a way that survives minification. + // jqLiteEmpty takes no arguments but is a setter. + if (fn !== jqLiteEmpty && + (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) { + if (isObject(arg1)) { + + // we are a write, but the object properties are the key/values + for (i = 0; i < nodeCount; i++) { + if (fn === jqLiteData) { + // data() takes the whole object in jQuery + fn(this[i], arg1); + } else { + for (key in arg1) { + fn(this[i], key, arg1[key]); + } + } + } + // return self for chaining + return this; + } else { + // we are a read, so read the first child. + // TODO: do we still need this? + var value = fn.$dv; + // Only if we have $dv do we iterate over all, otherwise it is just the first element. + var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount; + for (var j = 0; j < jj; j++) { + var nodeValue = fn(this[j], arg1, arg2); + value = value ? value + nodeValue : nodeValue; + } + return value; + } + } else { + // we are a write, so apply to all children + for (i = 0; i < nodeCount; i++) { + fn(this[i], arg1, arg2); + } + // return self for chaining + return this; + } + }; +}); + +function createEventHandler(element, events) { + var eventHandler = function (event, type) { + if (!event.preventDefault) { + event.preventDefault = function() { + event.returnValue = false; //ie + }; + } + + if (!event.stopPropagation) { + event.stopPropagation = function() { + event.cancelBubble = true; //ie + }; + } + + if (!event.target) { + event.target = event.srcElement || document; + } + + if (isUndefined(event.defaultPrevented)) { + var prevent = event.preventDefault; + event.preventDefault = function() { + event.defaultPrevented = true; + prevent.call(event); + }; + event.defaultPrevented = false; + } + + event.isDefaultPrevented = function() { + return event.defaultPrevented || event.returnValue === false; + }; + + // Copy event handlers in case event handlers array is modified during execution. + var eventHandlersCopy = shallowCopy(events[type || event.type] || []); + + forEach(eventHandlersCopy, function(fn) { + fn.call(element, event); + }); + + // Remove monkey-patched methods (IE), + // as they would cause memory leaks in IE8. + if (msie <= 8) { + // IE7/8 does not allow to delete property on native object + event.preventDefault = null; + event.stopPropagation = null; + event.isDefaultPrevented = null; + } else { + // It shouldn't affect normal browsers (native methods are defined on prototype). + delete event.preventDefault; + delete event.stopPropagation; + delete event.isDefaultPrevented; + } + }; + eventHandler.elem = element; + return eventHandler; +} + +////////////////////////////////////////// +// Functions iterating traversal. +// These functions chain results into a single +// selector. +////////////////////////////////////////// +forEach({ + removeData: jqLiteRemoveData, + + dealoc: jqLiteDealoc, + + on: function onFn(element, type, fn, unsupported){ + if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); + + var events = jqLiteExpandoStore(element, 'events'), + handle = jqLiteExpandoStore(element, 'handle'); + + if (!events) jqLiteExpandoStore(element, 'events', events = {}); + if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); + + forEach(type.split(' '), function(type){ + var eventFns = events[type]; + + if (!eventFns) { + if (type == 'mouseenter' || type == 'mouseleave') { + var contains = document.body.contains || document.body.compareDocumentPosition ? + function( a, b ) { + // jshint bitwise: false + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + events[type] = []; + + // Refer to jQuery's implementation of mouseenter & mouseleave + // Read about mouseenter and mouseleave: + // http://www.quirksmode.org/js/events_mouse.html#link8 + var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}; + + onFn(element, eventmap[type], function(event) { + var target = this, related = event.relatedTarget; + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !contains(target, related)) ){ + handle(event, type); + } + }); + + } else { + addEventListenerFn(element, type, handle); + events[type] = []; + } + eventFns = events[type]; + } + eventFns.push(fn); + }); + }, + + off: jqLiteOff, + + one: function(element, type, fn) { + element = jqLite(element); + + //add the listener twice so that when it is called + //you can remove the original function and still be + //able to call element.off(ev, fn) normally + element.on(type, function onFn() { + element.off(type, fn); + element.off(type, onFn); + }); + element.on(type, fn); + }, + + replaceWith: function(element, replaceNode) { + var index, parent = element.parentNode; + jqLiteDealoc(element); + forEach(new JQLite(replaceNode), function(node){ + if (index) { + parent.insertBefore(node, index.nextSibling); + } else { + parent.replaceChild(node, element); + } + index = node; + }); + }, + + children: function(element) { + var children = []; + forEach(element.childNodes, function(element){ + if (element.nodeType === 1) + children.push(element); + }); + return children; + }, + + contents: function(element) { + return element.contentDocument || element.childNodes || []; + }, + + append: function(element, node) { + forEach(new JQLite(node), function(child){ + if (element.nodeType === 1 || element.nodeType === 11) { + element.appendChild(child); + } + }); + }, + + prepend: function(element, node) { + if (element.nodeType === 1) { + var index = element.firstChild; + forEach(new JQLite(node), function(child){ + element.insertBefore(child, index); + }); + } + }, + + wrap: function(element, wrapNode) { + wrapNode = jqLite(wrapNode)[0]; + var parent = element.parentNode; + if (parent) { + parent.replaceChild(wrapNode, element); + } + wrapNode.appendChild(element); + }, + + remove: function(element) { + jqLiteDealoc(element); + var parent = element.parentNode; + if (parent) parent.removeChild(element); + }, + + after: function(element, newElement) { + var index = element, parent = element.parentNode; + forEach(new JQLite(newElement), function(node){ + parent.insertBefore(node, index.nextSibling); + index = node; + }); + }, + + addClass: jqLiteAddClass, + removeClass: jqLiteRemoveClass, + + toggleClass: function(element, selector, condition) { + if (selector) { + forEach(selector.split(' '), function(className){ + var classCondition = condition; + if (isUndefined(classCondition)) { + classCondition = !jqLiteHasClass(element, className); + } + (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); + }); + } + }, + + parent: function(element) { + var parent = element.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + + next: function(element) { + if (element.nextElementSibling) { + return element.nextElementSibling; + } + + // IE8 doesn't have nextElementSibling + var elm = element.nextSibling; + while (elm != null && elm.nodeType !== 1) { + elm = elm.nextSibling; + } + return elm; + }, + + find: function(element, selector) { + if (element.getElementsByTagName) { + return element.getElementsByTagName(selector); + } else { + return []; + } + }, + + clone: jqLiteClone, + + triggerHandler: function(element, event, extraParameters) { + + var dummyEvent, eventFnsCopy, handlerArgs; + var eventName = event.type || event; + var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName]; + + if (eventFns) { + + // Create a dummy event to pass to the handlers + dummyEvent = { + preventDefault: function() { this.defaultPrevented = true; }, + isDefaultPrevented: function() { return this.defaultPrevented === true; }, + stopPropagation: noop, + type: eventName, + target: element + }; + + // If a custom event was provided then extend our dummy event with it + if (event.type) { + dummyEvent = extend(dummyEvent, event); + } + + // Copy event handlers in case event handlers array is modified during execution. + eventFnsCopy = shallowCopy(eventFns); + handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; + + forEach(eventFnsCopy, function(fn) { + fn.apply(element, handlerArgs); + }); + + } + } +}, function(fn, name){ + /** + * chaining functions + */ + JQLite.prototype[name] = function(arg1, arg2, arg3) { + var value; + for(var i=0; i < this.length; i++) { + if (isUndefined(value)) { + value = fn(this[i], arg1, arg2, arg3); + if (isDefined(value)) { + // any function which returns a value needs to be wrapped + value = jqLite(value); + } + } else { + jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); + } + } + return isDefined(value) ? value : this; + }; + + // bind legacy bind/unbind to on/off + JQLite.prototype.bind = JQLite.prototype.on; + JQLite.prototype.unbind = JQLite.prototype.off; +}); + +/** + * Computes a hash of an 'obj'. + * Hash of a: + * string is string + * number is number as string + * object is either result of calling $$hashKey function on the object or uniquely generated id, + * that is also assigned to the $$hashKey property of the object. + * + * @param obj + * @returns {string} hash string such that the same input will have the same hash string. + * The resulting string key is in 'type:hashKey' format. + */ +function hashKey(obj, nextUidFn) { + var objType = typeof obj, + key; + + if (objType == 'function' || (objType == 'object' && obj !== null)) { + if (typeof (key = obj.$$hashKey) == 'function') { + // must invoke on object to keep the right this + key = obj.$$hashKey(); + } else if (key === undefined) { + key = obj.$$hashKey = (nextUidFn || nextUid)(); + } + } else { + key = obj; + } + + return objType + ':' + key; +} + +/** + * HashMap which can use objects as keys + */ +function HashMap(array, isolatedUid) { + if (isolatedUid) { + var uid = 0; + this.nextUid = function() { + return ++uid; + }; + } + forEach(array, this.put, this); +} +HashMap.prototype = { + /** + * Store key value pair + * @param key key to store can be any type + * @param value value to store can be any type + */ + put: function(key, value) { + this[hashKey(key, this.nextUid)] = value; + }, + + /** + * @param key + * @returns {Object} the value for the key + */ + get: function(key) { + return this[hashKey(key, this.nextUid)]; + }, + + /** + * Remove the key/value pair + * @param key + */ + remove: function(key) { + var value = this[key = hashKey(key, this.nextUid)]; + delete this[key]; + return value; + } +}; + +/** + * @ngdoc function + * @module ng + * @name angular.injector + * @kind function + * + * @description + * Creates an injector function that can be used for retrieving services as well as for + * dependency injection (see {@link guide/di dependency injection}). + * + + * @param {Array.} modules A list of module functions or their aliases. See + * {@link angular.module}. The `ng` module must be explicitly added. + * @returns {function()} Injector function. See {@link auto.$injector $injector}. + * + * @example + * Typical usage + * ```js + * // create an injector + * var $injector = angular.injector(['ng']); + * + * // use the injector to kick off your application + * // use the type inference to auto inject arguments, or use implicit injection + * $injector.invoke(function($rootScope, $compile, $document){ + * $compile($document)($rootScope); + * $rootScope.$digest(); + * }); + * ``` + * + * Sometimes you want to get access to the injector of a currently running Angular app + * from outside Angular. Perhaps, you want to inject and compile some markup after the + * application has been bootstrapped. You can do this using the extra `injector()` added + * to JQuery/jqLite elements. See {@link angular.element}. + * + * *This is fairly rare but could be the case if a third party library is injecting the + * markup.* + * + * In the following example a new block of HTML containing a `ng-controller` + * directive is added to the end of the document body by JQuery. We then compile and link + * it into the current AngularJS scope. + * + * ```js + * var $div = $('
{{content.label}}
'); + * $(document.body).append($div); + * + * angular.element(document).injector().invoke(function($compile) { + * var scope = angular.element($div).scope(); + * $compile($div)(scope); + * }); + * ``` + */ + + +/** + * @ngdoc module + * @name auto + * @description + * + * Implicit module which gets automatically added to each {@link auto.$injector $injector}. + */ + +var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; +var FN_ARG_SPLIT = /,/; +var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; +var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; +var $injectorMinErr = minErr('$injector'); +function annotate(fn) { + var $inject, + fnText, + argDecl, + last; + + if (typeof fn === 'function') { + if (!($inject = fn.$inject)) { + $inject = []; + if (fn.length) { + fnText = fn.toString().replace(STRIP_COMMENTS, ''); + argDecl = fnText.match(FN_ARGS); + forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ + arg.replace(FN_ARG, function(all, underscore, name){ + $inject.push(name); + }); + }); + } + fn.$inject = $inject; + } + } else if (isArray(fn)) { + last = fn.length - 1; + assertArgFn(fn[last], 'fn'); + $inject = fn.slice(0, last); + } else { + assertArgFn(fn, 'fn', true); + } + return $inject; +} + +/////////////////////////////////////// + +/** + * @ngdoc service + * @name $injector + * @kind function + * + * @description + * + * `$injector` is used to retrieve object instances as defined by + * {@link auto.$provide provider}, instantiate types, invoke methods, + * and load modules. + * + * The following always holds true: + * + * ```js + * var $injector = angular.injector(); + * expect($injector.get('$injector')).toBe($injector); + * expect($injector.invoke(function($injector){ + * return $injector; + * }).toBe($injector); + * ``` + * + * # Injection Function Annotation + * + * JavaScript does not have annotations, and annotations are needed for dependency injection. The + * following are all valid ways of annotating function with injection arguments and are equivalent. + * + * ```js + * // inferred (only works if code not minified/obfuscated) + * $injector.invoke(function(serviceA){}); + * + * // annotated + * function explicit(serviceA) {}; + * explicit.$inject = ['serviceA']; + * $injector.invoke(explicit); + * + * // inline + * $injector.invoke(['serviceA', function(serviceA){}]); + * ``` + * + * ## Inference + * + * In JavaScript calling `toString()` on a function returns the function definition. The definition + * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with + * minification, and obfuscation tools since these tools change the argument names. + * + * ## `$inject` Annotation + * By adding an `$inject` property onto a function the injection parameters can be specified. + * + * ## Inline + * As an array of injection names, where the last item in the array is the function to call. + */ + +/** + * @ngdoc method + * @name $injector#get + * + * @description + * Return an instance of the service. + * + * @param {string} name The name of the instance to retrieve. + * @return {*} The instance. + */ + +/** + * @ngdoc method + * @name $injector#invoke + * + * @description + * Invoke the method and supply the method arguments from the `$injector`. + * + * @param {!Function} fn The function to invoke. Function parameters are injected according to the + * {@link guide/di $inject Annotation} rules. + * @param {Object=} self The `this` for the invoked method. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. + * @returns {*} the value returned by the invoked `fn` function. + */ + +/** + * @ngdoc method + * @name $injector#has + * + * @description + * Allows the user to query if the particular service exists. + * + * @param {string} Name of the service to query. + * @returns {boolean} returns true if injector has given service. + */ + +/** + * @ngdoc method + * @name $injector#instantiate + * @description + * Create a new instance of JS type. The method takes a constructor function, invokes the new + * operator, and supplies all of the arguments to the constructor function as specified by the + * constructor annotation. + * + * @param {Function} Type Annotated constructor function. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. + * @returns {Object} new instance of `Type`. + */ + +/** + * @ngdoc method + * @name $injector#annotate + * + * @description + * Returns an array of service names which the function is requesting for injection. This API is + * used by the injector to determine which services need to be injected into the function when the + * function is invoked. There are three ways in which the function can be annotated with the needed + * dependencies. + * + * # Argument names + * + * The simplest form is to extract the dependencies from the arguments of the function. This is done + * by converting the function into a string using `toString()` method and extracting the argument + * names. + * ```js + * // Given + * function MyController($scope, $route) { + * // ... + * } + * + * // Then + * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); + * ``` + * + * This method does not work with code minification / obfuscation. For this reason the following + * annotation strategies are supported. + * + * # The `$inject` property + * + * If a function has an `$inject` property and its value is an array of strings, then the strings + * represent names of services to be injected into the function. + * ```js + * // Given + * var MyController = function(obfuscatedScope, obfuscatedRoute) { + * // ... + * } + * // Define function dependencies + * MyController['$inject'] = ['$scope', '$route']; + * + * // Then + * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); + * ``` + * + * # The array notation + * + * It is often desirable to inline Injected functions and that's when setting the `$inject` property + * is very inconvenient. In these situations using the array notation to specify the dependencies in + * a way that survives minification is a better choice: + * + * ```js + * // We wish to write this (not minification / obfuscation safe) + * injector.invoke(function($compile, $rootScope) { + * // ... + * }); + * + * // We are forced to write break inlining + * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { + * // ... + * }; + * tmpFn.$inject = ['$compile', '$rootScope']; + * injector.invoke(tmpFn); + * + * // To better support inline function the inline annotation is supported + * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { + * // ... + * }]); + * + * // Therefore + * expect(injector.annotate( + * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) + * ).toEqual(['$compile', '$rootScope']); + * ``` + * + * @param {Function|Array.} fn Function for which dependent service names need to + * be retrieved as described above. + * + * @returns {Array.} The names of the services which the function requires. + */ + + + + +/** + * @ngdoc service + * @name $provide + * + * @description + * + * The {@link auto.$provide $provide} service has a number of methods for registering components + * with the {@link auto.$injector $injector}. Many of these functions are also exposed on + * {@link angular.Module}. + * + * An Angular **service** is a singleton object created by a **service factory**. These **service + * factories** are functions which, in turn, are created by a **service provider**. + * The **service providers** are constructor functions. When instantiated they must contain a + * property called `$get`, which holds the **service factory** function. + * + * When you request a service, the {@link auto.$injector $injector} is responsible for finding the + * correct **service provider**, instantiating it and then calling its `$get` **service factory** + * function to get the instance of the **service**. + * + * Often services have no configuration options and there is no need to add methods to the service + * provider. The provider will be no more than a constructor function with a `$get` property. For + * these cases the {@link auto.$provide $provide} service has additional helper methods to register + * services without specifying a provider. + * + * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the + * {@link auto.$injector $injector} + * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by + * providers and services. + * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by + * services, not providers. + * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, + * that will be wrapped in a **service provider** object, whose `$get` property will contain the + * given factory function. + * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class` + * that will be wrapped in a **service provider** object, whose `$get` property will instantiate + * a new object using the given constructor function. + * + * See the individual methods for more information and examples. + */ + +/** + * @ngdoc method + * @name $provide#provider + * @description + * + * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions + * are constructor functions, whose instances are responsible for "providing" a factory for a + * service. + * + * Service provider names start with the name of the service they provide followed by `Provider`. + * For example, the {@link ng.$log $log} service has a provider called + * {@link ng.$logProvider $logProvider}. + * + * Service provider objects can have additional methods which allow configuration of the provider + * and its service. Importantly, you can configure what kind of service is created by the `$get` + * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a + * method {@link ng.$logProvider#debugEnabled debugEnabled} + * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the + * console or not. + * + * @param {string} name The name of the instance. NOTE: the provider will be available under `name + + 'Provider'` key. + * @param {(Object|function())} provider If the provider is: + * + * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using + * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. + * - `Constructor`: a new instance of the provider will be created using + * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. + * + * @returns {Object} registered provider instance + + * @example + * + * The following example shows how to create a simple event tracking service and register it using + * {@link auto.$provide#provider $provide.provider()}. + * + * ```js + * // Define the eventTracker provider + * function EventTrackerProvider() { + * var trackingUrl = '/track'; + * + * // A provider method for configuring where the tracked events should been saved + * this.setTrackingUrl = function(url) { + * trackingUrl = url; + * }; + * + * // The service factory function + * this.$get = ['$http', function($http) { + * var trackedEvents = {}; + * return { + * // Call this to track an event + * event: function(event) { + * var count = trackedEvents[event] || 0; + * count += 1; + * trackedEvents[event] = count; + * return count; + * }, + * // Call this to save the tracked events to the trackingUrl + * save: function() { + * $http.post(trackingUrl, trackedEvents); + * } + * }; + * }]; + * } + * + * describe('eventTracker', function() { + * var postSpy; + * + * beforeEach(module(function($provide) { + * // Register the eventTracker provider + * $provide.provider('eventTracker', EventTrackerProvider); + * })); + * + * beforeEach(module(function(eventTrackerProvider) { + * // Configure eventTracker provider + * eventTrackerProvider.setTrackingUrl('/custom-track'); + * })); + * + * it('tracks events', inject(function(eventTracker) { + * expect(eventTracker.event('login')).toEqual(1); + * expect(eventTracker.event('login')).toEqual(2); + * })); + * + * it('saves to the tracking url', inject(function(eventTracker, $http) { + * postSpy = spyOn($http, 'post'); + * eventTracker.event('login'); + * eventTracker.save(); + * expect(postSpy).toHaveBeenCalled(); + * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); + * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); + * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); + * })); + * }); + * ``` + */ + +/** + * @ngdoc method + * @name $provide#factory + * @description + * + * Register a **service factory**, which will be called to return the service instance. + * This is short for registering a service where its provider consists of only a `$get` property, + * which is the given service factory function. + * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to + * configure your service in a provider. + * + * @param {string} name The name of the instance. + * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand + * for `$provide.provider(name, {$get: $getFn})`. + * @returns {Object} registered provider instance + * + * @example + * Here is an example of registering a service + * ```js + * $provide.factory('ping', ['$http', function($http) { + * return function ping() { + * return $http.send('/ping'); + * }; + * }]); + * ``` + * You would then inject and use this service like this: + * ```js + * someModule.controller('Ctrl', ['ping', function(ping) { + * ping(); + * }]); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#service + * @description + * + * Register a **service constructor**, which will be invoked with `new` to create the service + * instance. + * This is short for registering a service where its provider's `$get` property is the service + * constructor function that will be used to instantiate the service instance. + * + * You should use {@link auto.$provide#service $provide.service(class)} if you define your service + * as a type/class. + * + * @param {string} name The name of the instance. + * @param {Function} constructor A class (constructor function) that will be instantiated. + * @returns {Object} registered provider instance + * + * @example + * Here is an example of registering a service using + * {@link auto.$provide#service $provide.service(class)}. + * ```js + * var Ping = function($http) { + * this.$http = $http; + * }; + * + * Ping.$inject = ['$http']; + * + * Ping.prototype.send = function() { + * return this.$http.get('/ping'); + * }; + * $provide.service('ping', Ping); + * ``` + * You would then inject and use this service like this: + * ```js + * someModule.controller('Ctrl', ['ping', function(ping) { + * ping.send(); + * }]); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#value + * @description + * + * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a + * number, an array, an object or a function. This is short for registering a service where its + * provider's `$get` property is a factory function that takes no arguments and returns the **value + * service**. + * + * Value services are similar to constant services, except that they cannot be injected into a + * module configuration function (see {@link angular.Module#config}) but they can be overridden by + * an Angular + * {@link auto.$provide#decorator decorator}. + * + * @param {string} name The name of the instance. + * @param {*} value The value. + * @returns {Object} registered provider instance + * + * @example + * Here are some examples of creating value services. + * ```js + * $provide.value('ADMIN_USER', 'admin'); + * + * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); + * + * $provide.value('halfOf', function(value) { + * return value / 2; + * }); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#constant + * @description + * + * Register a **constant service**, such as a string, a number, an array, an object or a function, + * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be + * injected into a module configuration function (see {@link angular.Module#config}) and it cannot + * be overridden by an Angular {@link auto.$provide#decorator decorator}. + * + * @param {string} name The name of the constant. + * @param {*} value The constant value. + * @returns {Object} registered instance + * + * @example + * Here a some examples of creating constants: + * ```js + * $provide.constant('SHARD_HEIGHT', 306); + * + * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); + * + * $provide.constant('double', function(value) { + * return value * 2; + * }); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#decorator + * @description + * + * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator + * intercepts the creation of a service, allowing it to override or modify the behaviour of the + * service. The object returned by the decorator may be the original service, or a new service + * object which replaces or wraps and delegates to the original service. + * + * @param {string} name The name of the service to decorate. + * @param {function()} decorator This function will be invoked when the service needs to be + * instantiated and should return the decorated service instance. The function is called using + * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. + * Local injection arguments: + * + * * `$delegate` - The original service instance, which can be monkey patched, configured, + * decorated or delegated to. + * + * @example + * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting + * calls to {@link ng.$log#error $log.warn()}. + * ```js + * $provide.decorator('$log', ['$delegate', function($delegate) { + * $delegate.warn = $delegate.error; + * return $delegate; + * }]); + * ``` + */ + + +function createInjector(modulesToLoad) { + var INSTANTIATING = {}, + providerSuffix = 'Provider', + path = [], + loadedModules = new HashMap([], true), + providerCache = { + $provide: { + provider: supportObject(provider), + factory: supportObject(factory), + service: supportObject(service), + value: supportObject(value), + constant: supportObject(constant), + decorator: decorator + } + }, + providerInjector = (providerCache.$injector = + createInternalInjector(providerCache, function() { + throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); + })), + instanceCache = {}, + instanceInjector = (instanceCache.$injector = + createInternalInjector(instanceCache, function(servicename) { + var provider = providerInjector.get(servicename + providerSuffix); + return instanceInjector.invoke(provider.$get, provider); + })); + + + forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); + + return instanceInjector; + + //////////////////////////////////// + // $provider + //////////////////////////////////// + + function supportObject(delegate) { + return function(key, value) { + if (isObject(key)) { + forEach(key, reverseParams(delegate)); + } else { + return delegate(key, value); + } + }; + } + + function provider(name, provider_) { + assertNotHasOwnProperty(name, 'service'); + if (isFunction(provider_) || isArray(provider_)) { + provider_ = providerInjector.instantiate(provider_); + } + if (!provider_.$get) { + throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); + } + return providerCache[name + providerSuffix] = provider_; + } + + function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } + + function service(name, constructor) { + return factory(name, ['$injector', function($injector) { + return $injector.instantiate(constructor); + }]); + } + + function value(name, val) { return factory(name, valueFn(val)); } + + function constant(name, value) { + assertNotHasOwnProperty(name, 'constant'); + providerCache[name] = value; + instanceCache[name] = value; + } + + function decorator(serviceName, decorFn) { + var origProvider = providerInjector.get(serviceName + providerSuffix), + orig$get = origProvider.$get; + + origProvider.$get = function() { + var origInstance = instanceInjector.invoke(orig$get, origProvider); + return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); + }; + } + + //////////////////////////////////// + // Module Loading + //////////////////////////////////// + function loadModules(modulesToLoad){ + var runBlocks = [], moduleFn, invokeQueue, i, ii; + forEach(modulesToLoad, function(module) { + if (loadedModules.get(module)) return; + loadedModules.put(module, true); + + try { + if (isString(module)) { + moduleFn = angularModule(module); + runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); + + for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { + var invokeArgs = invokeQueue[i], + provider = providerInjector.get(invokeArgs[0]); + + provider[invokeArgs[1]].apply(provider, invokeArgs[2]); + } + } else if (isFunction(module)) { + runBlocks.push(providerInjector.invoke(module)); + } else if (isArray(module)) { + runBlocks.push(providerInjector.invoke(module)); + } else { + assertArgFn(module, 'module'); + } + } catch (e) { + if (isArray(module)) { + module = module[module.length - 1]; + } + if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { + // Safari & FF's stack traces don't contain error.message content + // unlike those of Chrome and IE + // So if stack doesn't contain message, we create a new string that contains both. + // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. + /* jshint -W022 */ + e = e.message + '\n' + e.stack; + } + throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", + module, e.stack || e.message || e); + } + }); + return runBlocks; + } + + //////////////////////////////////// + // internal Injector + //////////////////////////////////// + + function createInternalInjector(cache, factory) { + + function getService(serviceName) { + if (cache.hasOwnProperty(serviceName)) { + if (cache[serviceName] === INSTANTIATING) { + throw $injectorMinErr('cdep', 'Circular dependency found: {0}', + serviceName + ' <- ' + path.join(' <- ')); + } + return cache[serviceName]; + } else { + try { + path.unshift(serviceName); + cache[serviceName] = INSTANTIATING; + return cache[serviceName] = factory(serviceName); + } catch (err) { + if (cache[serviceName] === INSTANTIATING) { + delete cache[serviceName]; + } + throw err; + } finally { + path.shift(); + } + } + } + + function invoke(fn, self, locals){ + var args = [], + $inject = annotate(fn), + length, i, + key; + + for(i = 0, length = $inject.length; i < length; i++) { + key = $inject[i]; + if (typeof key !== 'string') { + throw $injectorMinErr('itkn', + 'Incorrect injection token! Expected service name as string, got {0}', key); + } + args.push( + locals && locals.hasOwnProperty(key) + ? locals[key] + : getService(key) + ); + } + if (isArray(fn)) { + fn = fn[length]; + } + + // http://jsperf.com/angularjs-invoke-apply-vs-switch + // #5388 + return fn.apply(self, args); + } + + function instantiate(Type, locals) { + var Constructor = function() {}, + instance, returnedValue; + + // Check if Type is annotated and use just the given function at n-1 as parameter + // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); + Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; + instance = new Constructor(); + returnedValue = invoke(Type, instance, locals); + + return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; + } + + return { + invoke: invoke, + instantiate: instantiate, + get: getService, + annotate: annotate, + has: function(name) { + return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); + } + }; + } +} + +/** + * @ngdoc service + * @name $anchorScroll + * @kind function + * @requires $window + * @requires $location + * @requires $rootScope + * + * @description + * When called, it checks current value of `$location.hash()` and scrolls to the related element, + * according to rules specified in + * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). + * + * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor. + * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. + * + * @example + + +
+ Go to bottom + You're at the bottom! +
+
+ + function ScrollCtrl($scope, $location, $anchorScroll) { + $scope.gotoBottom = function (){ + // set the location.hash to the id of + // the element you wish to scroll to. + $location.hash('bottom'); + + // call $anchorScroll() + $anchorScroll(); + }; + } + + + #scrollArea { + height: 350px; + overflow: auto; + } + + #bottom { + display: block; + margin-top: 2000px; + } + +
+ */ +function $AnchorScrollProvider() { + + var autoScrollingEnabled = true; + + this.disableAutoScrolling = function() { + autoScrollingEnabled = false; + }; + + this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { + var document = $window.document; + + // helper function to get first anchor from a NodeList + // can't use filter.filter, as it accepts only instances of Array + // and IE can't convert NodeList to an array using [].slice + // TODO(vojta): use filter if we change it to accept lists as well + function getFirstAnchor(list) { + var result = null; + forEach(list, function(element) { + if (!result && lowercase(element.nodeName) === 'a') result = element; + }); + return result; + } + + function scroll() { + var hash = $location.hash(), elm; + + // empty hash, scroll to the top of the page + if (!hash) $window.scrollTo(0, 0); + + // element with given id + else if ((elm = document.getElementById(hash))) elm.scrollIntoView(); + + // first anchor with given name :-D + else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView(); + + // no element and hash == 'top', scroll to the top of the page + else if (hash === 'top') $window.scrollTo(0, 0); + } + + // does not scroll when user clicks on anchor link that is currently on + // (no url change, no $location.hash() change), browser native does scroll + if (autoScrollingEnabled) { + $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, + function autoScrollWatchAction() { + $rootScope.$evalAsync(scroll); + }); + } + + return scroll; + }]; +} + +var $animateMinErr = minErr('$animate'); + +/** + * @ngdoc provider + * @name $animateProvider + * + * @description + * Default implementation of $animate that doesn't perform any animations, instead just + * synchronously performs DOM + * updates and calls done() callbacks. + * + * In order to enable animations the ngAnimate module has to be loaded. + * + * To see the functional implementation check out src/ngAnimate/animate.js + */ +var $AnimateProvider = ['$provide', function($provide) { + + + this.$$selectors = {}; + + + /** + * @ngdoc method + * @name $animateProvider#register + * + * @description + * Registers a new injectable animation factory function. The factory function produces the + * animation object which contains callback functions for each event that is expected to be + * animated. + * + * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` + * must be called once the element animation is complete. If a function is returned then the + * animation service will use this function to cancel the animation whenever a cancel event is + * triggered. + * + * + * ```js + * return { + * eventFn : function(element, done) { + * //code to run the animation + * //once complete, then run done() + * return function cancellationFunction() { + * //code to cancel the animation + * } + * } + * } + * ``` + * + * @param {string} name The name of the animation. + * @param {Function} factory The factory function that will be executed to return the animation + * object. + */ + this.register = function(name, factory) { + var key = name + '-animation'; + if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel', + "Expecting class selector starting with '.' got '{0}'.", name); + this.$$selectors[name.substr(1)] = key; + $provide.factory(key, factory); + }; + + /** + * @ngdoc method + * @name $animateProvider#classNameFilter + * + * @description + * Sets and/or returns the CSS class regular expression that is checked when performing + * an animation. Upon bootstrap the classNameFilter value is not set at all and will + * therefore enable $animate to attempt to perform an animation on any element. + * When setting the classNameFilter value, animations will only be performed on elements + * that successfully match the filter expression. This in turn can boost performance + * for low-powered devices as well as applications containing a lot of structural operations. + * @param {RegExp=} expression The className expression which will be checked against all animations + * @return {RegExp} The current CSS className expression value. If null then there is no expression value + */ + this.classNameFilter = function(expression) { + if(arguments.length === 1) { + this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; + } + return this.$$classNameFilter; + }; + + this.$get = ['$timeout', '$$asyncCallback', function($timeout, $$asyncCallback) { + + function async(fn) { + fn && $$asyncCallback(fn); + } + + /** + * + * @ngdoc service + * @name $animate + * @description The $animate service provides rudimentary DOM manipulation functions to + * insert, remove and move elements within the DOM, as well as adding and removing classes. + * This service is the core service used by the ngAnimate $animator service which provides + * high-level animation hooks for CSS and JavaScript. + * + * $animate is available in the AngularJS core, however, the ngAnimate module must be included + * to enable full out animation support. Otherwise, $animate will only perform simple DOM + * manipulation operations. + * + * To learn more about enabling animation support, click here to visit the {@link ngAnimate + * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service + * page}. + */ + return { + + /** + * + * @ngdoc method + * @name $animate#enter + * @kind function + * @description Inserts the element into the DOM either after the `after` element or within + * the `parent` element. Once complete, the done() callback will be fired (if provided). + * @param {DOMElement} element the element which will be inserted into the DOM + * @param {DOMElement} parent the parent element which will append the element as + * a child (if the after element is not present) + * @param {DOMElement} after the sibling element which will append the element + * after itself + * @param {Function=} done callback function that will be called after the element has been + * inserted into the DOM + */ + enter : function(element, parent, after, done) { + if (after) { + after.after(element); + } else { + if (!parent || !parent[0]) { + parent = after.parent(); + } + parent.append(element); + } + async(done); + }, + + /** + * + * @ngdoc method + * @name $animate#leave + * @kind function + * @description Removes the element from the DOM. Once complete, the done() callback will be + * fired (if provided). + * @param {DOMElement} element the element which will be removed from the DOM + * @param {Function=} done callback function that will be called after the element has been + * removed from the DOM + */ + leave : function(element, done) { + element.remove(); + async(done); + }, + + /** + * + * @ngdoc method + * @name $animate#move + * @kind function + * @description Moves the position of the provided element within the DOM to be placed + * either after the `after` element or inside of the `parent` element. Once complete, the + * done() callback will be fired (if provided). + * + * @param {DOMElement} element the element which will be moved around within the + * DOM + * @param {DOMElement} parent the parent element where the element will be + * inserted into (if the after element is not present) + * @param {DOMElement} after the sibling element where the element will be + * positioned next to + * @param {Function=} done the callback function (if provided) that will be fired after the + * element has been moved to its new position + */ + move : function(element, parent, after, done) { + // Do not remove element before insert. Removing will cause data associated with the + // element to be dropped. Insert will implicitly do the remove. + this.enter(element, parent, after, done); + }, + + /** + * + * @ngdoc method + * @name $animate#addClass + * @kind function + * @description Adds the provided className CSS class value to the provided element. Once + * complete, the done() callback will be fired (if provided). + * @param {DOMElement} element the element which will have the className value + * added to it + * @param {string} className the CSS class which will be added to the element + * @param {Function=} done the callback function (if provided) that will be fired after the + * className value has been added to the element + */ + addClass : function(element, className, done) { + className = isString(className) ? + className : + isArray(className) ? className.join(' ') : ''; + forEach(element, function (element) { + jqLiteAddClass(element, className); + }); + async(done); + }, + + /** + * + * @ngdoc method + * @name $animate#removeClass + * @kind function + * @description Removes the provided className CSS class value from the provided element. + * Once complete, the done() callback will be fired (if provided). + * @param {DOMElement} element the element which will have the className value + * removed from it + * @param {string} className the CSS class which will be removed from the element + * @param {Function=} done the callback function (if provided) that will be fired after the + * className value has been removed from the element + */ + removeClass : function(element, className, done) { + className = isString(className) ? + className : + isArray(className) ? className.join(' ') : ''; + forEach(element, function (element) { + jqLiteRemoveClass(element, className); + }); + async(done); + }, + + /** + * + * @ngdoc method + * @name $animate#setClass + * @kind function + * @description Adds and/or removes the given CSS classes to and from the element. + * Once complete, the done() callback will be fired (if provided). + * @param {DOMElement} element the element which will have its CSS classes changed + * removed from it + * @param {string} add the CSS classes which will be added to the element + * @param {string} remove the CSS class which will be removed from the element + * @param {Function=} done the callback function (if provided) that will be fired after the + * CSS classes have been set on the element + */ + setClass : function(element, add, remove, done) { + forEach(element, function (element) { + jqLiteAddClass(element, add); + jqLiteRemoveClass(element, remove); + }); + async(done); + }, + + enabled : noop + }; + }]; +}]; + +function $$AsyncCallbackProvider(){ + this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) { + return $$rAF.supported + ? function(fn) { return $$rAF(fn); } + : function(fn) { + return $timeout(fn, 0, false); + }; + }]; +} + +/** + * ! This is a private undocumented service ! + * + * @name $browser + * @requires $log + * @description + * This object has two goals: + * + * - hide all the global state in the browser caused by the window object + * - abstract away all the browser specific features and inconsistencies + * + * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` + * service, which can be used for convenient testing of the application without the interaction with + * the real browser apis. + */ +/** + * @param {object} window The global window object. + * @param {object} document jQuery wrapped document. + * @param {function()} XHR XMLHttpRequest constructor. + * @param {object} $log console.log or an object with the same interface. + * @param {object} $sniffer $sniffer service + */ +function Browser(window, document, $log, $sniffer) { + var self = this, + rawDocument = document[0], + location = window.location, + history = window.history, + setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, + pendingDeferIds = {}; + + self.isMock = false; + + var outstandingRequestCount = 0; + var outstandingRequestCallbacks = []; + + // TODO(vojta): remove this temporary api + self.$$completeOutstandingRequest = completeOutstandingRequest; + self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; + + /** + * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` + * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. + */ + function completeOutstandingRequest(fn) { + try { + fn.apply(null, sliceArgs(arguments, 1)); + } finally { + outstandingRequestCount--; + if (outstandingRequestCount === 0) { + while(outstandingRequestCallbacks.length) { + try { + outstandingRequestCallbacks.pop()(); + } catch (e) { + $log.error(e); + } + } + } + } + } + + /** + * @private + * Note: this method is used only by scenario runner + * TODO(vojta): prefix this method with $$ ? + * @param {function()} callback Function that will be called when no outstanding request + */ + self.notifyWhenNoOutstandingRequests = function(callback) { + // force browser to execute all pollFns - this is needed so that cookies and other pollers fire + // at some deterministic time in respect to the test runner's actions. Leaving things up to the + // regular poller would result in flaky tests. + forEach(pollFns, function(pollFn){ pollFn(); }); + + if (outstandingRequestCount === 0) { + callback(); + } else { + outstandingRequestCallbacks.push(callback); + } + }; + + ////////////////////////////////////////////////////////////// + // Poll Watcher API + ////////////////////////////////////////////////////////////// + var pollFns = [], + pollTimeout; + + /** + * @name $browser#addPollFn + * + * @param {function()} fn Poll function to add + * + * @description + * Adds a function to the list of functions that poller periodically executes, + * and starts polling if not started yet. + * + * @returns {function()} the added function + */ + self.addPollFn = function(fn) { + if (isUndefined(pollTimeout)) startPoller(100, setTimeout); + pollFns.push(fn); + return fn; + }; + + /** + * @param {number} interval How often should browser call poll functions (ms) + * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. + * + * @description + * Configures the poller to run in the specified intervals, using the specified + * setTimeout fn and kicks it off. + */ + function startPoller(interval, setTimeout) { + (function check() { + forEach(pollFns, function(pollFn){ pollFn(); }); + pollTimeout = setTimeout(check, interval); + })(); + } + + ////////////////////////////////////////////////////////////// + // URL API + ////////////////////////////////////////////////////////////// + + var lastBrowserUrl = location.href, + baseElement = document.find('base'), + newLocation = null; + + /** + * @name $browser#url + * + * @description + * GETTER: + * Without any argument, this method just returns current value of location.href. + * + * SETTER: + * With at least one argument, this method sets url to new value. + * If html5 history api supported, pushState/replaceState is used, otherwise + * location.href/location.replace is used. + * Returns its own instance to allow chaining + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to change url. + * + * @param {string} url New url (when used as setter) + * @param {boolean=} replace Should new url replace current history record ? + */ + self.url = function(url, replace) { + // Android Browser BFCache causes location, history reference to become stale. + if (location !== window.location) location = window.location; + if (history !== window.history) history = window.history; + + // setter + if (url) { + if (lastBrowserUrl == url) return; + lastBrowserUrl = url; + if ($sniffer.history) { + if (replace) history.replaceState(null, '', url); + else { + history.pushState(null, '', url); + // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462 + baseElement.attr('href', baseElement.attr('href')); + } + } else { + newLocation = url; + if (replace) { + location.replace(url); + } else { + location.href = url; + } + } + return self; + // getter + } else { + // - newLocation is a workaround for an IE7-9 issue with location.replace and location.href + // methods not updating location.href synchronously. + // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 + return newLocation || location.href.replace(/%27/g,"'"); + } + }; + + var urlChangeListeners = [], + urlChangeInit = false; + + function fireUrlChange() { + newLocation = null; + if (lastBrowserUrl == self.url()) return; + + lastBrowserUrl = self.url(); + forEach(urlChangeListeners, function(listener) { + listener(self.url()); + }); + } + + /** + * @name $browser#onUrlChange + * + * @description + * Register callback function that will be called, when url changes. + * + * It's only called when the url is changed from outside of angular: + * - user types different url into address bar + * - user clicks on history (forward/back) button + * - user clicks on a link + * + * It's not called when url is changed by $browser.url() method + * + * The listener gets called with new url as parameter. + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to monitor url changes in angular apps. + * + * @param {function(string)} listener Listener function to be called when url changes. + * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. + */ + self.onUrlChange = function(callback) { + // TODO(vojta): refactor to use node's syntax for events + if (!urlChangeInit) { + // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) + // don't fire popstate when user change the address bar and don't fire hashchange when url + // changed by push/replaceState + + // html5 history api - popstate event + if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange); + // hashchange event + if ($sniffer.hashchange) jqLite(window).on('hashchange', fireUrlChange); + // polling + else self.addPollFn(fireUrlChange); + + urlChangeInit = true; + } + + urlChangeListeners.push(callback); + return callback; + }; + + /** + * Checks whether the url has changed outside of Angular. + * Needs to be exported to be able to check for changes that have been done in sync, + * as hashchange/popstate events fire in async. + */ + self.$$checkUrlChange = fireUrlChange; + + ////////////////////////////////////////////////////////////// + // Misc API + ////////////////////////////////////////////////////////////// + + /** + * @name $browser#baseHref + * + * @description + * Returns current + * (always relative - without domain) + * + * @returns {string} The current base href + */ + self.baseHref = function() { + var href = baseElement.attr('href'); + return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : ''; + }; + + ////////////////////////////////////////////////////////////// + // Cookies API + ////////////////////////////////////////////////////////////// + var lastCookies = {}; + var lastCookieString = ''; + var cookiePath = self.baseHref(); + + /** + * @name $browser#cookies + * + * @param {string=} name Cookie name + * @param {string=} value Cookie value + * + * @description + * The cookies method provides a 'private' low level access to browser cookies. + * It is not meant to be used directly, use the $cookie service instead. + * + * The return values vary depending on the arguments that the method was called with as follows: + * + * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify + * it + * - cookies(name, value) -> set name to value, if value is undefined delete the cookie + * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that + * way) + * + * @returns {Object} Hash of all cookies (if called without any parameter) + */ + self.cookies = function(name, value) { + /* global escape: false, unescape: false */ + var cookieLength, cookieArray, cookie, i, index; + + if (name) { + if (value === undefined) { + rawDocument.cookie = escape(name) + "=;path=" + cookiePath + + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; + } else { + if (isString(value)) { + cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + + ';path=' + cookiePath).length + 1; + + // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: + // - 300 cookies + // - 20 cookies per unique domain + // - 4096 bytes per cookie + if (cookieLength > 4096) { + $log.warn("Cookie '"+ name + + "' possibly not set or overflowed because it was too large ("+ + cookieLength + " > 4096 bytes)!"); + } + } + } + } else { + if (rawDocument.cookie !== lastCookieString) { + lastCookieString = rawDocument.cookie; + cookieArray = lastCookieString.split("; "); + lastCookies = {}; + + for (i = 0; i < cookieArray.length; i++) { + cookie = cookieArray[i]; + index = cookie.indexOf('='); + if (index > 0) { //ignore nameless cookies + name = unescape(cookie.substring(0, index)); + // the first value that is seen for a cookie is the most + // specific one. values for the same cookie name that + // follow are for less specific paths. + if (lastCookies[name] === undefined) { + lastCookies[name] = unescape(cookie.substring(index + 1)); + } + } + } + } + return lastCookies; + } + }; + + + /** + * @name $browser#defer + * @param {function()} fn A function, who's execution should be deferred. + * @param {number=} [delay=0] of milliseconds to defer the function execution. + * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. + * + * @description + * Executes a fn asynchronously via `setTimeout(fn, delay)`. + * + * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using + * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed + * via `$browser.defer.flush()`. + * + */ + self.defer = function(fn, delay) { + var timeoutId; + outstandingRequestCount++; + timeoutId = setTimeout(function() { + delete pendingDeferIds[timeoutId]; + completeOutstandingRequest(fn); + }, delay || 0); + pendingDeferIds[timeoutId] = true; + return timeoutId; + }; + + + /** + * @name $browser#defer.cancel + * + * @description + * Cancels a deferred task identified with `deferId`. + * + * @param {*} deferId Token returned by the `$browser.defer` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + self.defer.cancel = function(deferId) { + if (pendingDeferIds[deferId]) { + delete pendingDeferIds[deferId]; + clearTimeout(deferId); + completeOutstandingRequest(noop); + return true; + } + return false; + }; + +} + +function $BrowserProvider(){ + this.$get = ['$window', '$log', '$sniffer', '$document', + function( $window, $log, $sniffer, $document){ + return new Browser($window, $document, $log, $sniffer); + }]; +} + +/** + * @ngdoc service + * @name $cacheFactory + * + * @description + * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to + * them. + * + * ```js + * + * var cache = $cacheFactory('cacheId'); + * expect($cacheFactory.get('cacheId')).toBe(cache); + * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); + * + * cache.put("key", "value"); + * cache.put("another key", "another value"); + * + * // We've specified no options on creation + * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); + * + * ``` + * + * + * @param {string} cacheId Name or id of the newly created cache. + * @param {object=} options Options object that specifies the cache behavior. Properties: + * + * - `{number=}` `capacity` — turns the cache into LRU cache. + * + * @returns {object} Newly created cache object with the following set of methods: + * + * - `{object}` `info()` — Returns id, size, and options of cache. + * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns + * it. + * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. + * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. + * - `{void}` `removeAll()` — Removes all cached values. + * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. + * + * @example + + +
+ + + + +

Cached Values

+
+ + : + +
+ +

Cache Info

+
+ + : + +
+
+
+ + angular.module('cacheExampleApp', []). + controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { + $scope.keys = []; + $scope.cache = $cacheFactory('cacheId'); + $scope.put = function(key, value) { + if ($scope.cache.get(key) === undefined) { + $scope.keys.push(key); + } + $scope.cache.put(key, value === undefined ? null : value); + }; + }]); + + + p { + margin: 10px 0 3px; + } + +
+ */ +function $CacheFactoryProvider() { + + this.$get = function() { + var caches = {}; + + function cacheFactory(cacheId, options) { + if (cacheId in caches) { + throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId); + } + + var size = 0, + stats = extend({}, options, {id: cacheId}), + data = {}, + capacity = (options && options.capacity) || Number.MAX_VALUE, + lruHash = {}, + freshEnd = null, + staleEnd = null; + + /** + * @ngdoc type + * @name $cacheFactory.Cache + * + * @description + * A cache object used to store and retrieve data, primarily used by + * {@link $http $http} and the {@link ng.directive:script script} directive to cache + * templates and other data. + * + * ```js + * angular.module('superCache') + * .factory('superCache', ['$cacheFactory', function($cacheFactory) { + * return $cacheFactory('super-cache'); + * }]); + * ``` + * + * Example test: + * + * ```js + * it('should behave like a cache', inject(function(superCache) { + * superCache.put('key', 'value'); + * superCache.put('another key', 'another value'); + * + * expect(superCache.info()).toEqual({ + * id: 'super-cache', + * size: 2 + * }); + * + * superCache.remove('another key'); + * expect(superCache.get('another key')).toBeUndefined(); + * + * superCache.removeAll(); + * expect(superCache.info()).toEqual({ + * id: 'super-cache', + * size: 0 + * }); + * })); + * ``` + */ + return caches[cacheId] = { + + /** + * @ngdoc method + * @name $cacheFactory.Cache#put + * @kind function + * + * @description + * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be + * retrieved later, and incrementing the size of the cache if the key was not already + * present in the cache. If behaving like an LRU cache, it will also remove stale + * entries from the set. + * + * It will not insert undefined values into the cache. + * + * @param {string} key the key under which the cached data is stored. + * @param {*} value the value to store alongside the key. If it is undefined, the key + * will not be stored. + * @returns {*} the value stored. + */ + put: function(key, value) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); + + refresh(lruEntry); + } + + if (isUndefined(value)) return; + if (!(key in data)) size++; + data[key] = value; + + if (size > capacity) { + this.remove(staleEnd.key); + } + + return value; + }, + + /** + * @ngdoc method + * @name $cacheFactory.Cache#get + * @kind function + * + * @description + * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. + * + * @param {string} key the key of the data to be retrieved + * @returns {*} the value stored. + */ + get: function(key) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + refresh(lruEntry); + } + + return data[key]; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#remove + * @kind function + * + * @description + * Removes an entry from the {@link $cacheFactory.Cache Cache} object. + * + * @param {string} key the key of the entry to be removed + */ + remove: function(key) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + if (lruEntry == freshEnd) freshEnd = lruEntry.p; + if (lruEntry == staleEnd) staleEnd = lruEntry.n; + link(lruEntry.n,lruEntry.p); + + delete lruHash[key]; + } + + delete data[key]; + size--; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#removeAll + * @kind function + * + * @description + * Clears the cache object of any entries. + */ + removeAll: function() { + data = {}; + size = 0; + lruHash = {}; + freshEnd = staleEnd = null; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#destroy + * @kind function + * + * @description + * Destroys the {@link $cacheFactory.Cache Cache} object entirely, + * removing it from the {@link $cacheFactory $cacheFactory} set. + */ + destroy: function() { + data = null; + stats = null; + lruHash = null; + delete caches[cacheId]; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#info + * @kind function + * + * @description + * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}. + * + * @returns {object} an object with the following properties: + *
    + *
  • **id**: the id of the cache instance
  • + *
  • **size**: the number of entries kept in the cache instance
  • + *
  • **...**: any additional properties from the options object when creating the + * cache.
  • + *
+ */ + info: function() { + return extend({}, stats, {size: size}); + } + }; + + + /** + * makes the `entry` the freshEnd of the LRU linked list + */ + function refresh(entry) { + if (entry != freshEnd) { + if (!staleEnd) { + staleEnd = entry; + } else if (staleEnd == entry) { + staleEnd = entry.n; + } + + link(entry.n, entry.p); + link(entry, freshEnd); + freshEnd = entry; + freshEnd.n = null; + } + } + + + /** + * bidirectionally links two entries of the LRU linked list + */ + function link(nextEntry, prevEntry) { + if (nextEntry != prevEntry) { + if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify + if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify + } + } + } + + + /** + * @ngdoc method + * @name $cacheFactory#info + * + * @description + * Get information about all the caches that have been created + * + * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` + */ + cacheFactory.info = function() { + var info = {}; + forEach(caches, function(cache, cacheId) { + info[cacheId] = cache.info(); + }); + return info; + }; + + + /** + * @ngdoc method + * @name $cacheFactory#get + * + * @description + * Get access to a cache object by the `cacheId` used when it was created. + * + * @param {string} cacheId Name or id of a cache to access. + * @returns {object} Cache object identified by the cacheId or undefined if no such cache. + */ + cacheFactory.get = function(cacheId) { + return caches[cacheId]; + }; + + + return cacheFactory; + }; +} + +/** + * @ngdoc service + * @name $templateCache + * + * @description + * The first time a template is used, it is loaded in the template cache for quick retrieval. You + * can load templates directly into the cache in a `script` tag, or by consuming the + * `$templateCache` service directly. + * + * Adding via the `script` tag: + * + * ```html + * + * ``` + * + * **Note:** the `script` tag containing the template does not need to be included in the `head` of + * the document, but it must be below the `ng-app` definition. + * + * Adding via the $templateCache service: + * + * ```js + * var myApp = angular.module('myApp', []); + * myApp.run(function($templateCache) { + * $templateCache.put('templateId.html', 'This is the content of the template'); + * }); + * ``` + * + * To retrieve the template later, simply use it in your HTML: + * ```html + *
+ * ``` + * + * or get it via Javascript: + * ```js + * $templateCache.get('templateId.html') + * ``` + * + * See {@link ng.$cacheFactory $cacheFactory}. + * + */ +function $TemplateCacheProvider() { + this.$get = ['$cacheFactory', function($cacheFactory) { + return $cacheFactory('templates'); + }]; +} + +/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! + * + * DOM-related variables: + * + * - "node" - DOM Node + * - "element" - DOM Element or Node + * - "$node" or "$element" - jqLite-wrapped node or element + * + * + * Compiler related stuff: + * + * - "linkFn" - linking fn of a single directive + * - "nodeLinkFn" - function that aggregates all linking fns for a particular node + * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node + * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) + */ + + +/** + * @ngdoc service + * @name $compile + * @kind function + * + * @description + * Compiles an HTML string or DOM into a template and produces a template function, which + * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. + * + * The compilation is a process of walking the DOM tree and matching DOM elements to + * {@link ng.$compileProvider#directive directives}. + * + *
+ * **Note:** This document is an in-depth reference of all directive options. + * For a gentle introduction to directives with examples of common use cases, + * see the {@link guide/directive directive guide}. + *
+ * + * ## Comprehensive Directive API + * + * There are many different options for a directive. + * + * The difference resides in the return value of the factory function. + * You can either return a "Directive Definition Object" (see below) that defines the directive properties, + * or just the `postLink` function (all other properties will have the default values). + * + *
+ * **Best Practice:** It's recommended to use the "directive definition object" form. + *
+ * + * Here's an example directive declared with a Directive Definition Object: + * + * ```js + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * priority: 0, + * template: '
', // or // function(tElement, tAttrs) { ... }, + * // or + * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, + * transclude: false, + * restrict: 'A', + * scope: false, + * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, + * controllerAs: 'stringAlias', + * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], + * compile: function compile(tElement, tAttrs, transclude) { + * return { + * pre: function preLink(scope, iElement, iAttrs, controller) { ... }, + * post: function postLink(scope, iElement, iAttrs, controller) { ... } + * } + * // or + * // return function postLink( ... ) { ... } + * }, + * // or + * // link: { + * // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, + * // post: function postLink(scope, iElement, iAttrs, controller) { ... } + * // } + * // or + * // link: function postLink( ... ) { ... } + * }; + * return directiveDefinitionObject; + * }); + * ``` + * + *
+ * **Note:** Any unspecified options will use the default value. You can see the default values below. + *
+ * + * Therefore the above can be simplified as: + * + * ```js + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * link: function postLink(scope, iElement, iAttrs) { ... } + * }; + * return directiveDefinitionObject; + * // or + * // return function postLink(scope, iElement, iAttrs) { ... } + * }); + * ``` + * + * + * + * ### Directive Definition Object + * + * The directive definition object provides instructions to the {@link ng.$compile + * compiler}. The attributes are: + * + * #### `priority` + * When there are multiple directives defined on a single DOM element, sometimes it + * is necessary to specify the order in which the directives are applied. The `priority` is used + * to sort the directives before their `compile` functions get called. Priority is defined as a + * number. Directives with greater numerical `priority` are compiled first. Pre-link functions + * are also run in priority order, but post-link functions are run in reverse order. The order + * of directives with the same priority is undefined. The default priority is `0`. + * + * #### `terminal` + * If set to true then the current `priority` will be the last set of directives + * which will execute (any directives at the current priority will still execute + * as the order of execution on same `priority` is undefined). + * + * #### `scope` + * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the + * same element request a new scope, only one new scope is created. The new scope rule does not + * apply for the root of the template since the root of the template always gets a new scope. + * + * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from + * normal scope in that it does not prototypically inherit from the parent scope. This is useful + * when creating reusable components, which should not accidentally read or modify data in the + * parent scope. + * + * The 'isolate' scope takes an object hash which defines a set of local scope properties + * derived from the parent scope. These local properties are useful for aliasing values for + * templates. Locals definition is a hash of local scope property to its source: + * + * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is + * always a string since DOM attributes are strings. If no `attr` name is specified then the + * attribute name is assumed to be the same as the local name. + * Given `` and widget definition + * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect + * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the + * `localName` property on the widget scope. The `name` is read from the parent scope (not + * component scope). + * + * * `=` or `=attr` - set up bi-directional binding between a local scope property and the + * parent scope property of name defined via the value of the `attr` attribute. If no `attr` + * name is specified then the attribute name is assumed to be the same as the local name. + * Given `` and widget definition of + * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the + * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected + * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent + * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You + * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. + * + * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. + * If no `attr` name is specified then the attribute name is assumed to be the same as the + * local name. Given `` and widget definition of + * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to + * a function wrapper for the `count = count + value` expression. Often it's desirable to + * pass data from the isolated scope via an expression to the parent scope, this can be + * done by passing a map of local variable names and values into the expression wrapper fn. + * For example, if the expression is `increment(amount)` then we can specify the amount value + * by calling the `localFn` as `localFn({amount: 22})`. + * + * + * + * #### `controller` + * Controller constructor function. The controller is instantiated before the + * pre-linking phase and it is shared with other directives (see + * `require` attribute). This allows the directives to communicate with each other and augment + * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: + * + * * `$scope` - Current scope associated with the element + * * `$element` - Current element + * * `$attrs` - Current attributes object for the element + * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope. + * The scope can be overridden by an optional first argument. + * `function([scope], cloneLinkingFn)`. + * + * + * #### `require` + * Require another directive and inject its controller as the fourth argument to the linking function. The + * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the + * injected argument will be an array in corresponding order. If no such directive can be + * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with: + * + * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. + * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. + * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. + * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass + * `null` to the `link` fn if not found. + * + * + * #### `controllerAs` + * Controller alias at the directive scope. An alias for the controller so it + * can be referenced at the directive template. The directive needs to define a scope for this + * configuration to be used. Useful in the case when directive is used as component. + * + * + * #### `restrict` + * String of subset of `EACM` which restricts the directive to a specific directive + * declaration style. If omitted, the default (attributes only) is used. + * + * * `E` - Element name: `` + * * `A` - Attribute (default): `
` + * * `C` - Class: `
` + * * `M` - Comment: `` + * + * + * #### `template` + * HTML markup that may: + * * Replace the contents of the directive's element (default). + * * Replace the directive's element itself (if `replace` is true - DEPRECATED). + * * Wrap the contents of the directive's element (if `transclude` is true). + * + * Value may be: + * + * * A string. For example `
{{delete_str}}
`. + * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile` + * function api below) and returns a string value. + * + * + * #### `templateUrl` + * Same as `template` but the template is loaded from the specified URL. Because + * the template loading is asynchronous the compilation/linking is suspended until the template + * is loaded. + * + * You can specify `templateUrl` as a string representing the URL or as a function which takes two + * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns + * a string value representing the url. In either case, the template URL is passed through {@link + * api/ng.$sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. + * + * + * #### `replace` ([*DEPRECATED*!], will be removed in next major release) + * specify what the template should replace. Defaults to `false`. + * + * * `true` - the template will replace the directive's element. + * * `false` - the template will replace the contents of the directive's element. + * + * The replacement process migrates all of the attributes / classes from the old element to the new + * one. See the {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive + * Directives Guide} for an example. + * + * #### `transclude` + * compile the content of the element and make it available to the directive. + * Typically used with {@link ng.directive:ngTransclude + * ngTransclude}. The advantage of transclusion is that the linking function receives a + * transclusion function which is pre-bound to the correct scope. In a typical setup the widget + * creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate` + * scope. This makes it possible for the widget to have private state, and the transclusion to + * be bound to the parent (pre-`isolate`) scope. + * + * * `true` - transclude the content of the directive. + * * `'element'` - transclude the whole element including any directives defined at lower priority. + * + *
+ * **Note:** When testing an element transclude directive you must not place the directive at the root of the + * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives + * Testing Transclusion Directives}. + *
+ * + * #### `compile` + * + * ```js + * function compile(tElement, tAttrs, transclude) { ... } + * ``` + * + * The compile function deals with transforming the template DOM. Since most directives do not do + * template transformation, it is not used often. The compile function takes the following arguments: + * + * * `tElement` - template element - The element where the directive has been declared. It is + * safe to do template transformation on the element and child elements only. + * + * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared + * between all directive compile functions. + * + * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` + * + *
+ * **Note:** The template instance and the link instance may be different objects if the template has + * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that + * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration + * should be done in a linking function rather than in a compile function. + *
+ + *
+ * **Note:** The compile function cannot handle directives that recursively use themselves in their + * own templates or compile functions. Compiling these directives results in an infinite loop and a + * stack overflow errors. + * + * This can be avoided by manually using $compile in the postLink function to imperatively compile + * a directive's template instead of relying on automatic template compilation via `template` or + * `templateUrl` declaration or manual compilation inside the compile function. + *
+ * + *
+ * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it + * e.g. does not know about the right outer scope. Please use the transclude function that is passed + * to the link function instead. + *
+ + * A compile function can have a return value which can be either a function or an object. + * + * * returning a (post-link) function - is equivalent to registering the linking function via the + * `link` property of the config object when the compile function is empty. + * + * * returning an object with function(s) registered via `pre` and `post` properties - allows you to + * control when a linking function should be called during the linking phase. See info about + * pre-linking and post-linking functions below. + * + * + * #### `link` + * This property is used only if the `compile` property is not defined. + * + * ```js + * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } + * ``` + * + * The link function is responsible for registering DOM listeners as well as updating the DOM. It is + * executed after the template has been cloned. This is where most of the directive logic will be + * put. + * + * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the + * directive for registering {@link ng.$rootScope.Scope#$watch watches}. + * + * * `iElement` - instance element - The element where the directive is to be used. It is safe to + * manipulate the children of the element only in `postLink` function since the children have + * already been linked. + * + * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared + * between all directive linking functions. + * + * * `controller` - a controller instance - A controller instance if at least one directive on the + * element defines a controller. The controller is shared among all the directives, which allows + * the directives to use the controllers as a communication channel. + * + * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. + * The scope can be overridden by an optional first argument. This is the same as the `$transclude` + * parameter of directive controllers. + * `function([scope], cloneLinkingFn)`. + * + * + * #### Pre-linking function + * + * Executed before the child elements are linked. Not safe to do DOM transformation since the + * compiler linking function will fail to locate the correct elements for linking. + * + * #### Post-linking function + * + * Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function. + * + * + * ### Attributes + * + * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the + * `link()` or `compile()` functions. It has a variety of uses. + * + * accessing *Normalized attribute names:* + * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. + * the attributes object allows for normalized access to + * the attributes. + * + * * *Directive inter-communication:* All directives share the same instance of the attributes + * object which allows the directives to use the attributes object as inter directive + * communication. + * + * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object + * allowing other directives to read the interpolated value. + * + * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes + * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also + * the only way to easily get the actual value because during the linking phase the interpolation + * hasn't been evaluated yet and so the value is at this time set to `undefined`. + * + * ```js + * function linkingFn(scope, elm, attrs, ctrl) { + * // get the attribute value + * console.log(attrs.ngModel); + * + * // change the attribute + * attrs.$set('ngModel', 'new value'); + * + * // observe changes to interpolated attribute + * attrs.$observe('ngModel', function(value) { + * console.log('ngModel has changed value to ' + value); + * }); + * } + * ``` + * + * Below is an example using `$compileProvider`. + * + *
+ * **Note**: Typically directives are registered with `module.directive`. The example below is + * to illustrate how `$compile` works. + *
+ * + + + +
+
+
+
+
+
+ + it('should auto compile', function() { + var textarea = $('textarea'); + var output = $('div[compile]'); + // The initial state reads 'Hello Angular'. + expect(output.getText()).toBe('Hello Angular'); + textarea.clear(); + textarea.sendKeys('{{name}}!'); + expect(output.getText()).toBe('Angular!'); + }); + +
+ + * + * + * @param {string|DOMElement} element Element or HTML string to compile into a template function. + * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives. + * @param {number} maxPriority only apply directives lower than given priority (Only effects the + * root element(s), not their children) + * @returns {function(scope, cloneAttachFn=)} a link function which is used to bind template + * (a DOM element/tree) to a scope. Where: + * + * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. + * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the + * `template` and call the `cloneAttachFn` function allowing the caller to attach the + * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is + * called as:
`cloneAttachFn(clonedElement, scope)` where: + * + * * `clonedElement` - is a clone of the original `element` passed into the compiler. + * * `scope` - is the current scope with which the linking function is working with. + * + * Calling the linking function returns the element of the template. It is either the original + * element passed in, or the clone of the element if the `cloneAttachFn` is provided. + * + * After linking the view is not updated until after a call to $digest which typically is done by + * Angular automatically. + * + * If you need access to the bound view, there are two ways to do it: + * + * - If you are not asking the linking function to clone the template, create the DOM element(s) + * before you send them to the compiler and keep this reference around. + * ```js + * var element = $compile('

{{total}}

')(scope); + * ``` + * + * - if on the other hand, you need the element to be cloned, the view reference from the original + * example would not point to the clone, but rather to the original template that was cloned. In + * this case, you can access the clone via the cloneAttachFn: + * ```js + * var templateElement = angular.element('

{{total}}

'), + * scope = ....; + * + * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { + * //attach the clone to DOM document at the right place + * }); + * + * //now we have reference to the cloned DOM via `clonedElement` + * ``` + * + * + * For information on how the compiler works, see the + * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. + */ + +var $compileMinErr = minErr('$compile'); + +/** + * @ngdoc provider + * @name $compileProvider + * @kind function + * + * @description + */ +$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; +function $CompileProvider($provide, $$sanitizeUriProvider) { + var hasDirectives = {}, + Suffix = 'Directive', + COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/, + CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/; + + // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes + // The assumption is that future DOM event attribute names will begin with + // 'on' and be composed of only English letters. + var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; + + /** + * @ngdoc method + * @name $compileProvider#directive + * @kind function + * + * @description + * Register a new directive with the compiler. + * + * @param {string|Object} name Name of the directive in camel-case (i.e. ngBind which + * will match as ng-bind), or an object map of directives where the keys are the + * names and the values are the factories. + * @param {Function|Array} directiveFactory An injectable directive factory function. See + * {@link guide/directive} for more info. + * @returns {ng.$compileProvider} Self for chaining. + */ + this.directive = function registerDirective(name, directiveFactory) { + assertNotHasOwnProperty(name, 'directive'); + if (isString(name)) { + assertArg(directiveFactory, 'directiveFactory'); + if (!hasDirectives.hasOwnProperty(name)) { + hasDirectives[name] = []; + $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', + function($injector, $exceptionHandler) { + var directives = []; + forEach(hasDirectives[name], function(directiveFactory, index) { + try { + var directive = $injector.invoke(directiveFactory); + if (isFunction(directive)) { + directive = { compile: valueFn(directive) }; + } else if (!directive.compile && directive.link) { + directive.compile = valueFn(directive.link); + } + directive.priority = directive.priority || 0; + directive.index = index; + directive.name = directive.name || name; + directive.require = directive.require || (directive.controller && directive.name); + directive.restrict = directive.restrict || 'A'; + directives.push(directive); + } catch (e) { + $exceptionHandler(e); + } + }); + return directives; + }]); + } + hasDirectives[name].push(directiveFactory); + } else { + forEach(name, reverseParams(registerDirective)); + } + return this; + }; + + + /** + * @ngdoc method + * @name $compileProvider#aHrefSanitizationWhitelist + * @kind function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.aHrefSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp); + return this; + } else { + return $$sanitizeUriProvider.aHrefSanitizationWhitelist(); + } + }; + + + /** + * @ngdoc method + * @name $compileProvider#imgSrcSanitizationWhitelist + * @kind function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during img[src] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to img[src] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.imgSrcSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp); + return this; + } else { + return $$sanitizeUriProvider.imgSrcSanitizationWhitelist(); + } + }; + + this.$get = [ + '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', + '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri', + function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, + $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) { + + var Attributes = function(element, attr) { + this.$$element = element; + this.$attr = attr || {}; + }; + + Attributes.prototype = { + $normalize: directiveNormalize, + + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$addClass + * @kind function + * + * @description + * Adds the CSS class value specified by the classVal parameter to the element. If animations + * are enabled then an animation will be triggered for the class addition. + * + * @param {string} classVal The className value that will be added to the element + */ + $addClass : function(classVal) { + if(classVal && classVal.length > 0) { + $animate.addClass(this.$$element, classVal); + } + }, + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$removeClass + * @kind function + * + * @description + * Removes the CSS class value specified by the classVal parameter from the element. If + * animations are enabled then an animation will be triggered for the class removal. + * + * @param {string} classVal The className value that will be removed from the element + */ + $removeClass : function(classVal) { + if(classVal && classVal.length > 0) { + $animate.removeClass(this.$$element, classVal); + } + }, + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$updateClass + * @kind function + * + * @description + * Adds and removes the appropriate CSS class values to the element based on the difference + * between the new and old CSS class values (specified as newClasses and oldClasses). + * + * @param {string} newClasses The current CSS className value + * @param {string} oldClasses The former CSS className value + */ + $updateClass : function(newClasses, oldClasses) { + var toAdd = tokenDifference(newClasses, oldClasses); + var toRemove = tokenDifference(oldClasses, newClasses); + + if(toAdd.length === 0) { + $animate.removeClass(this.$$element, toRemove); + } else if(toRemove.length === 0) { + $animate.addClass(this.$$element, toAdd); + } else { + $animate.setClass(this.$$element, toAdd, toRemove); + } + }, + + /** + * Set a normalized attribute on the element in a way such that all directives + * can share the attribute. This function properly handles boolean attributes. + * @param {string} key Normalized key. (ie ngAttribute) + * @param {string|boolean} value The value to set. If `null` attribute will be deleted. + * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. + * Defaults to true. + * @param {string=} attrName Optional none normalized name. Defaults to key. + */ + $set: function(key, value, writeAttr, attrName) { + // TODO: decide whether or not to throw an error if "class" + //is set through this function since it may cause $updateClass to + //become unstable. + + var booleanKey = getBooleanAttrName(this.$$element[0], key), + normalizedVal, + nodeName; + + if (booleanKey) { + this.$$element.prop(key, value); + attrName = booleanKey; + } + + this[key] = value; + + // translate normalized key to actual key + if (attrName) { + this.$attr[key] = attrName; + } else { + attrName = this.$attr[key]; + if (!attrName) { + this.$attr[key] = attrName = snake_case(key, '-'); + } + } + + nodeName = nodeName_(this.$$element); + + // sanitize a[href] and img[src] values + if ((nodeName === 'A' && key === 'href') || + (nodeName === 'IMG' && key === 'src')) { + this[key] = value = $$sanitizeUri(value, key === 'src'); + } + + if (writeAttr !== false) { + if (value === null || value === undefined) { + this.$$element.removeAttr(attrName); + } else { + this.$$element.attr(attrName, value); + } + } + + // fire observers + var $$observers = this.$$observers; + $$observers && forEach($$observers[key], function(fn) { + try { + fn(value); + } catch (e) { + $exceptionHandler(e); + } + }); + }, + + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$observe + * @kind function + * + * @description + * Observes an interpolated attribute. + * + * The observer function will be invoked once during the next `$digest` following + * compilation. The observer is then invoked whenever the interpolated value + * changes. + * + * @param {string} key Normalized key. (ie ngAttribute) . + * @param {function(interpolatedValue)} fn Function that will be called whenever + the interpolated value of the attribute changes. + * See the {@link guide/directive#Attributes Directives} guide for more info. + * @returns {function()} the `fn` parameter. + */ + $observe: function(key, fn) { + var attrs = this, + $$observers = (attrs.$$observers || (attrs.$$observers = {})), + listeners = ($$observers[key] || ($$observers[key] = [])); + + listeners.push(fn); + $rootScope.$evalAsync(function() { + if (!listeners.$$inter) { + // no one registered attribute interpolation function, so lets call it manually + fn(attrs[key]); + } + }); + return fn; + } + }; + + var startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') + ? identity + : function denormalizeTemplate(template) { + return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); + }, + NG_ATTR_BINDING = /^ngAttr[A-Z]/; + + + return compile; + + //================================ + + function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, + previousCompileContext) { + if (!($compileNodes instanceof jqLite)) { + // jquery always rewraps, whereas we need to preserve the original selector so that we can + // modify it. + $compileNodes = jqLite($compileNodes); + } + // We can not compile top level text elements since text nodes can be merged and we will + // not be able to attach scope data to them, so we will wrap them in + forEach($compileNodes, function(node, index){ + if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) { + $compileNodes[index] = node = jqLite(node).wrap('').parent()[0]; + } + }); + var compositeLinkFn = + compileNodes($compileNodes, transcludeFn, $compileNodes, + maxPriority, ignoreDirective, previousCompileContext); + safeAddClass($compileNodes, 'ng-scope'); + return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn){ + assertArg(scope, 'scope'); + // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart + // and sometimes changes the structure of the DOM. + var $linkNode = cloneConnectFn + ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! + : $compileNodes; + + forEach(transcludeControllers, function(instance, name) { + $linkNode.data('$' + name + 'Controller', instance); + }); + + // Attach scope only to non-text nodes. + for(var i = 0, ii = $linkNode.length; i + addDirective(directives, + directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective); + + // iterate over the attributes + for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, + j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { + var attrStartName = false; + var attrEndName = false; + + attr = nAttrs[j]; + if (!msie || msie >= 8 || attr.specified) { + name = attr.name; + value = trim(attr.value); + + // support ngAttr attribute binding + ngAttrName = directiveNormalize(name); + if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { + name = snake_case(ngAttrName.substr(6), '-'); + } + + var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); + if (ngAttrName === directiveNName + 'Start') { + attrStartName = name; + attrEndName = name.substr(0, name.length - 5) + 'end'; + name = name.substr(0, name.length - 6); + } + + nName = directiveNormalize(name.toLowerCase()); + attrsMap[nName] = name; + if (isNgAttr || !attrs.hasOwnProperty(nName)) { + attrs[nName] = value; + if (getBooleanAttrName(node, nName)) { + attrs[nName] = true; // presence means true + } + } + addAttrInterpolateDirective(node, directives, value, nName); + addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, + attrEndName); + } + } + + // use class as directive + className = node.className; + if (isString(className) && className !== '') { + while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { + nName = directiveNormalize(match[2]); + if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) { + attrs[nName] = trim(match[3]); + } + className = className.substr(match.index + match[0].length); + } + } + break; + case 3: /* Text Node */ + addTextInterpolateDirective(directives, node.nodeValue); + break; + case 8: /* Comment */ + try { + match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); + if (match) { + nName = directiveNormalize(match[1]); + if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { + attrs[nName] = trim(match[2]); + } + } + } catch (e) { + // turns out that under some circumstances IE9 throws errors when one attempts to read + // comment's node value. + // Just ignore it and continue. (Can't seem to reproduce in test case.) + } + break; + } + + directives.sort(byPriority); + return directives; + } + + /** + * Given a node with an directive-start it collects all of the siblings until it finds + * directive-end. + * @param node + * @param attrStart + * @param attrEnd + * @returns {*} + */ + function groupScan(node, attrStart, attrEnd) { + var nodes = []; + var depth = 0; + if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { + var startNode = node; + do { + if (!node) { + throw $compileMinErr('uterdir', + "Unterminated attribute, found '{0}' but no matching '{1}' found.", + attrStart, attrEnd); + } + if (node.nodeType == 1 /** Element **/) { + if (node.hasAttribute(attrStart)) depth++; + if (node.hasAttribute(attrEnd)) depth--; + } + nodes.push(node); + node = node.nextSibling; + } while (depth > 0); + } else { + nodes.push(node); + } + + return jqLite(nodes); + } + + /** + * Wrapper for linking function which converts normal linking function into a grouped + * linking function. + * @param linkFn + * @param attrStart + * @param attrEnd + * @returns {Function} + */ + function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { + return function(scope, element, attrs, controllers, transcludeFn) { + element = groupScan(element[0], attrStart, attrEnd); + return linkFn(scope, element, attrs, controllers, transcludeFn); + }; + } + + /** + * Once the directives have been collected, their compile functions are executed. This method + * is responsible for inlining directive templates as well as terminating the application + * of the directives if the terminal directive has been reached. + * + * @param {Array} directives Array of collected directives to execute their compile function. + * this needs to be pre-sorted by priority order. + * @param {Node} compileNode The raw DOM node to apply the compile functions to + * @param {Object} templateAttrs The shared attribute function + * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the + * scope argument is auto-generated to the new + * child of the transcluded parent scope. + * @param {JQLite} jqCollection If we are working on the root of the compile tree then this + * argument has the root jqLite array so that we can replace nodes + * on it. + * @param {Object=} originalReplaceDirective An optional directive that will be ignored when + * compiling the transclusion. + * @param {Array.} preLinkFns + * @param {Array.} postLinkFns + * @param {Object} previousCompileContext Context used for previous compilation of the current + * node + * @returns {Function} linkFn + */ + function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, + jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, + previousCompileContext) { + previousCompileContext = previousCompileContext || {}; + + var terminalPriority = -Number.MAX_VALUE, + newScopeDirective, + controllerDirectives = previousCompileContext.controllerDirectives, + newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, + templateDirective = previousCompileContext.templateDirective, + nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, + hasTranscludeDirective = false, + hasTemplate = false, + hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective, + $compileNode = templateAttrs.$$element = jqLite(compileNode), + directive, + directiveName, + $template, + replaceDirective = originalReplaceDirective, + childTranscludeFn = transcludeFn, + linkFn, + directiveValue; + + // executes all directives on the current element + for(var i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + var attrStart = directive.$$start; + var attrEnd = directive.$$end; + + // collect multiblock sections + if (attrStart) { + $compileNode = groupScan(compileNode, attrStart, attrEnd); + } + $template = undefined; + + if (terminalPriority > directive.priority) { + break; // prevent further processing of directives + } + + if (directiveValue = directive.scope) { + newScopeDirective = newScopeDirective || directive; + + // skip the check for directives with async templates, we'll check the derived sync + // directive when the template arrives + if (!directive.templateUrl) { + assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, + $compileNode); + if (isObject(directiveValue)) { + newIsolateScopeDirective = directive; + } + } + } + + directiveName = directive.name; + + if (!directive.templateUrl && directive.controller) { + directiveValue = directive.controller; + controllerDirectives = controllerDirectives || {}; + assertNoDuplicate("'" + directiveName + "' controller", + controllerDirectives[directiveName], directive, $compileNode); + controllerDirectives[directiveName] = directive; + } + + if (directiveValue = directive.transclude) { + hasTranscludeDirective = true; + + // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. + // This option should only be used by directives that know how to safely handle element transclusion, + // where the transcluded nodes are added or replaced after linking. + if (!directive.$$tlb) { + assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); + nonTlbTranscludeDirective = directive; + } + + if (directiveValue == 'element') { + hasElementTranscludeDirective = true; + terminalPriority = directive.priority; + $template = $compileNode; + $compileNode = templateAttrs.$$element = + jqLite(document.createComment(' ' + directiveName + ': ' + + templateAttrs[directiveName] + ' ')); + compileNode = $compileNode[0]; + replaceWith(jqCollection, sliceArgs($template), compileNode); + + childTranscludeFn = compile($template, transcludeFn, terminalPriority, + replaceDirective && replaceDirective.name, { + // Don't pass in: + // - controllerDirectives - otherwise we'll create duplicates controllers + // - newIsolateScopeDirective or templateDirective - combining templates with + // element transclusion doesn't make sense. + // + // We need only nonTlbTranscludeDirective so that we prevent putting transclusion + // on the same element more than once. + nonTlbTranscludeDirective: nonTlbTranscludeDirective + }); + } else { + $template = jqLite(jqLiteClone(compileNode)).contents(); + $compileNode.empty(); // clear contents + childTranscludeFn = compile($template, transcludeFn); + } + } + + if (directive.template) { + hasTemplate = true; + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + + directiveValue = (isFunction(directive.template)) + ? directive.template($compileNode, templateAttrs) + : directive.template; + + directiveValue = denormalizeTemplate(directiveValue); + + if (directive.replace) { + replaceDirective = directive; + if (jqLiteIsTextNode(directiveValue)) { + $template = []; + } else { + $template = jqLite(trim(directiveValue)); + } + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== 1) { + throw $compileMinErr('tplrt', + "Template for directive '{0}' must have exactly one root element. {1}", + directiveName, ''); + } + + replaceWith(jqCollection, $compileNode, compileNode); + + var newTemplateAttrs = {$attr: {}}; + + // combine directives from the original node and from the template: + // - take the array of directives for this element + // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) + // - collect directives from the template and sort them by priority + // - combine directives as: processed + template + unprocessed + var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); + var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); + + if (newIsolateScopeDirective) { + markDirectivesAsIsolate(templateDirectives); + } + directives = directives.concat(templateDirectives).concat(unprocessedDirectives); + mergeTemplateAttributes(templateAttrs, newTemplateAttrs); + + ii = directives.length; + } else { + $compileNode.html(directiveValue); + } + } + + if (directive.templateUrl) { + hasTemplate = true; + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + + if (directive.replace) { + replaceDirective = directive; + } + + nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, + templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, { + controllerDirectives: controllerDirectives, + newIsolateScopeDirective: newIsolateScopeDirective, + templateDirective: templateDirective, + nonTlbTranscludeDirective: nonTlbTranscludeDirective + }); + ii = directives.length; + } else if (directive.compile) { + try { + linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); + if (isFunction(linkFn)) { + addLinkFns(null, linkFn, attrStart, attrEnd); + } else if (linkFn) { + addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd); + } + } catch (e) { + $exceptionHandler(e, startingTag($compileNode)); + } + } + + if (directive.terminal) { + nodeLinkFn.terminal = true; + terminalPriority = Math.max(terminalPriority, directive.priority); + } + + } + + nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; + nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; + nodeLinkFn.templateOnThisElement = hasTemplate; + nodeLinkFn.transclude = childTranscludeFn; + + previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; + + // might be normal or delayed nodeLinkFn depending on if templateUrl is present + return nodeLinkFn; + + //////////////////// + + function addLinkFns(pre, post, attrStart, attrEnd) { + if (pre) { + if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); + pre.require = directive.require; + pre.directiveName = directiveName; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + pre = cloneAndAnnotateFn(pre, {isolateScope: true}); + } + preLinkFns.push(pre); + } + if (post) { + if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); + post.require = directive.require; + post.directiveName = directiveName; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + post = cloneAndAnnotateFn(post, {isolateScope: true}); + } + postLinkFns.push(post); + } + } + + + function getControllers(directiveName, require, $element, elementControllers) { + var value, retrievalMethod = 'data', optional = false; + if (isString(require)) { + while((value = require.charAt(0)) == '^' || value == '?') { + require = require.substr(1); + if (value == '^') { + retrievalMethod = 'inheritedData'; + } + optional = optional || value == '?'; + } + value = null; + + if (elementControllers && retrievalMethod === 'data') { + value = elementControllers[require]; + } + value = value || $element[retrievalMethod]('$' + require + 'Controller'); + + if (!value && !optional) { + throw $compileMinErr('ctreq', + "Controller '{0}', required by directive '{1}', can't be found!", + require, directiveName); + } + return value; + } else if (isArray(require)) { + value = []; + forEach(require, function(require) { + value.push(getControllers(directiveName, require, $element, elementControllers)); + }); + } + return value; + } + + + function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { + var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn; + + attrs = (compileNode === linkNode) + ? templateAttrs + : shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); + $element = attrs.$$element; + + if (newIsolateScopeDirective) { + var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; + + isolateScope = scope.$new(true); + + if (templateDirective && (templateDirective === newIsolateScopeDirective || + templateDirective === newIsolateScopeDirective.$$originalDirective)) { + $element.data('$isolateScope', isolateScope); + } else { + $element.data('$isolateScopeNoTemplate', isolateScope); + } + + + + safeAddClass($element, 'ng-isolate-scope'); + + forEach(newIsolateScopeDirective.scope, function(definition, scopeName) { + var match = definition.match(LOCAL_REGEXP) || [], + attrName = match[3] || scopeName, + optional = (match[2] == '?'), + mode = match[1], // @, =, or & + lastValue, + parentGet, parentSet, compare; + + isolateScope.$$isolateBindings[scopeName] = mode + attrName; + + switch (mode) { + + case '@': + attrs.$observe(attrName, function(value) { + isolateScope[scopeName] = value; + }); + attrs.$$observers[attrName].$$scope = scope; + if( attrs[attrName] ) { + // If the attribute has been provided then we trigger an interpolation to ensure + // the value is there for use in the link fn + isolateScope[scopeName] = $interpolate(attrs[attrName])(scope); + } + break; + + case '=': + if (optional && !attrs[attrName]) { + return; + } + parentGet = $parse(attrs[attrName]); + if (parentGet.literal) { + compare = equals; + } else { + compare = function(a,b) { return a === b || (a !== a && b !== b); }; + } + parentSet = parentGet.assign || function() { + // reset the change, or we will throw this exception on every $digest + lastValue = isolateScope[scopeName] = parentGet(scope); + throw $compileMinErr('nonassign', + "Expression '{0}' used with directive '{1}' is non-assignable!", + attrs[attrName], newIsolateScopeDirective.name); + }; + lastValue = isolateScope[scopeName] = parentGet(scope); + isolateScope.$watch(function parentValueWatch() { + var parentValue = parentGet(scope); + if (!compare(parentValue, isolateScope[scopeName])) { + // we are out of sync and need to copy + if (!compare(parentValue, lastValue)) { + // parent changed and it has precedence + isolateScope[scopeName] = parentValue; + } else { + // if the parent can be assigned then do so + parentSet(scope, parentValue = isolateScope[scopeName]); + } + } + return lastValue = parentValue; + }, null, parentGet.literal); + break; + + case '&': + parentGet = $parse(attrs[attrName]); + isolateScope[scopeName] = function(locals) { + return parentGet(scope, locals); + }; + break; + + default: + throw $compileMinErr('iscp', + "Invalid isolate scope definition for directive '{0}'." + + " Definition: {... {1}: '{2}' ...}", + newIsolateScopeDirective.name, scopeName, definition); + } + }); + } + transcludeFn = boundTranscludeFn && controllersBoundTransclude; + if (controllerDirectives) { + forEach(controllerDirectives, function(directive) { + var locals = { + $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, + $element: $element, + $attrs: attrs, + $transclude: transcludeFn + }, controllerInstance; + + controller = directive.controller; + if (controller == '@') { + controller = attrs[directive.name]; + } + + controllerInstance = $controller(controller, locals); + // For directives with element transclusion the element is a comment, + // but jQuery .data doesn't support attaching data to comment nodes as it's hard to + // clean up (http://bugs.jquery.com/ticket/8335). + // Instead, we save the controllers for the element in a local hash and attach to .data + // later, once we have the actual element. + elementControllers[directive.name] = controllerInstance; + if (!hasElementTranscludeDirective) { + $element.data('$' + directive.name + 'Controller', controllerInstance); + } + + if (directive.controllerAs) { + locals.$scope[directive.controllerAs] = controllerInstance; + } + }); + } + + // PRELINKING + for(i = 0, ii = preLinkFns.length; i < ii; i++) { + try { + linkFn = preLinkFns[i]; + linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, + linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn); + } catch (e) { + $exceptionHandler(e, startingTag($element)); + } + } + + // RECURSION + // We only pass the isolate scope, if the isolate directive has a template, + // otherwise the child elements do not belong to the isolate directive. + var scopeToChild = scope; + if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { + scopeToChild = isolateScope; + } + childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); + + // POSTLINKING + for(i = postLinkFns.length - 1; i >= 0; i--) { + try { + linkFn = postLinkFns[i]; + linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, + linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn); + } catch (e) { + $exceptionHandler(e, startingTag($element)); + } + } + + // This is the function that is injected as `$transclude`. + function controllersBoundTransclude(scope, cloneAttachFn) { + var transcludeControllers; + + // no scope passed + if (arguments.length < 2) { + cloneAttachFn = scope; + scope = undefined; + } + + if (hasElementTranscludeDirective) { + transcludeControllers = elementControllers; + } + + return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers); + } + } + } + + function markDirectivesAsIsolate(directives) { + // mark all directives as needing isolate scope. + for (var j = 0, jj = directives.length; j < jj; j++) { + directives[j] = inherit(directives[j], {$$isolateScope: true}); + } + } + + /** + * looks up the directive and decorates it with exception handling and proper parameters. We + * call this the boundDirective. + * + * @param {string} name name of the directive to look up. + * @param {string} location The directive must be found in specific format. + * String containing any of theses characters: + * + * * `E`: element name + * * `A': attribute + * * `C`: class + * * `M`: comment + * @returns {boolean} true if directive was added. + */ + function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, + endAttrName) { + if (name === ignoreDirective) return null; + var match = null; + if (hasDirectives.hasOwnProperty(name)) { + for(var directive, directives = $injector.get(name + Suffix), + i = 0, ii = directives.length; i directive.priority) && + directive.restrict.indexOf(location) != -1) { + if (startAttrName) { + directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); + } + tDirectives.push(directive); + match = directive; + } + } catch(e) { $exceptionHandler(e); } + } + } + return match; + } + + + /** + * When the element is replaced with HTML template then the new attributes + * on the template need to be merged with the existing attributes in the DOM. + * The desired effect is to have both of the attributes present. + * + * @param {object} dst destination attributes (original DOM) + * @param {object} src source attributes (from the directive template) + */ + function mergeTemplateAttributes(dst, src) { + var srcAttr = src.$attr, + dstAttr = dst.$attr, + $element = dst.$$element; + + // reapply the old attributes to the new element + forEach(dst, function(value, key) { + if (key.charAt(0) != '$') { + if (src[key] && src[key] !== value) { + value += (key === 'style' ? ';' : ' ') + src[key]; + } + dst.$set(key, value, true, srcAttr[key]); + } + }); + + // copy the new attributes on the old attrs object + forEach(src, function(value, key) { + if (key == 'class') { + safeAddClass($element, value); + dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; + } else if (key == 'style') { + $element.attr('style', $element.attr('style') + ';' + value); + dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value; + // `dst` will never contain hasOwnProperty as DOM parser won't let it. + // You will get an "InvalidCharacterError: DOM Exception 5" error if you + // have an attribute like "has-own-property" or "data-has-own-property", etc. + } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { + dst[key] = value; + dstAttr[key] = srcAttr[key]; + } + }); + } + + + function compileTemplateUrl(directives, $compileNode, tAttrs, + $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { + var linkQueue = [], + afterTemplateNodeLinkFn, + afterTemplateChildLinkFn, + beforeTemplateCompileNode = $compileNode[0], + origAsyncDirective = directives.shift(), + // The fact that we have to copy and patch the directive seems wrong! + derivedSyncDirective = extend({}, origAsyncDirective, { + templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective + }), + templateUrl = (isFunction(origAsyncDirective.templateUrl)) + ? origAsyncDirective.templateUrl($compileNode, tAttrs) + : origAsyncDirective.templateUrl; + + $compileNode.empty(); + + $http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}). + success(function(content) { + var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; + + content = denormalizeTemplate(content); + + if (origAsyncDirective.replace) { + if (jqLiteIsTextNode(content)) { + $template = []; + } else { + $template = jqLite(trim(content)); + } + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== 1) { + throw $compileMinErr('tplrt', + "Template for directive '{0}' must have exactly one root element. {1}", + origAsyncDirective.name, templateUrl); + } + + tempTemplateAttrs = {$attr: {}}; + replaceWith($rootElement, $compileNode, compileNode); + var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); + + if (isObject(origAsyncDirective.scope)) { + markDirectivesAsIsolate(templateDirectives); + } + directives = templateDirectives.concat(directives); + mergeTemplateAttributes(tAttrs, tempTemplateAttrs); + } else { + compileNode = beforeTemplateCompileNode; + $compileNode.html(content); + } + + directives.unshift(derivedSyncDirective); + + afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, + childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, + previousCompileContext); + forEach($rootElement, function(node, i) { + if (node == compileNode) { + $rootElement[i] = $compileNode[0]; + } + }); + afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); + + while(linkQueue.length) { + var scope = linkQueue.shift(), + beforeTemplateLinkNode = linkQueue.shift(), + linkRootElement = linkQueue.shift(), + boundTranscludeFn = linkQueue.shift(), + linkNode = $compileNode[0]; + + if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { + var oldClasses = beforeTemplateLinkNode.className; + + if (!(previousCompileContext.hasElementTranscludeDirective && + origAsyncDirective.replace)) { + // it was cloned therefore we have to clone as well. + linkNode = jqLiteClone(compileNode); + } + + replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); + + // Copy in CSS classes from original node + safeAddClass(jqLite(linkNode), oldClasses); + } + if (afterTemplateNodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); + } else { + childBoundTranscludeFn = boundTranscludeFn; + } + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, + childBoundTranscludeFn); + } + linkQueue = null; + }). + error(function(response, code, headers, config) { + throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url); + }); + + return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { + var childBoundTranscludeFn = boundTranscludeFn; + if (linkQueue) { + linkQueue.push(scope); + linkQueue.push(node); + linkQueue.push(rootElement); + linkQueue.push(childBoundTranscludeFn); + } else { + if (afterTemplateNodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); + } + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); + } + }; + } + + + /** + * Sorting function for bound directives. + */ + function byPriority(a, b) { + var diff = b.priority - a.priority; + if (diff !== 0) return diff; + if (a.name !== b.name) return (a.name < b.name) ? -1 : 1; + return a.index - b.index; + } + + + function assertNoDuplicate(what, previousDirective, directive, element) { + if (previousDirective) { + throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}', + previousDirective.name, directive.name, what, startingTag(element)); + } + } + + + function addTextInterpolateDirective(directives, text) { + var interpolateFn = $interpolate(text, true); + if (interpolateFn) { + directives.push({ + priority: 0, + compile: function textInterpolateCompileFn(templateNode) { + // when transcluding a template that has bindings in the root + // then we don't have a parent and should do this in the linkFn + var parent = templateNode.parent(), hasCompileParent = parent.length; + if (hasCompileParent) safeAddClass(templateNode.parent(), 'ng-binding'); + + return function textInterpolateLinkFn(scope, node) { + var parent = node.parent(), + bindings = parent.data('$binding') || []; + bindings.push(interpolateFn); + parent.data('$binding', bindings); + if (!hasCompileParent) safeAddClass(parent, 'ng-binding'); + scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { + node[0].nodeValue = value; + }); + }; + } + }); + } + } + + + function getTrustedContext(node, attrNormalizedName) { + if (attrNormalizedName == "srcdoc") { + return $sce.HTML; + } + var tag = nodeName_(node); + // maction[xlink:href] can source SVG. It's not limited to . + if (attrNormalizedName == "xlinkHref" || + (tag == "FORM" && attrNormalizedName == "action") || + (tag != "IMG" && (attrNormalizedName == "src" || + attrNormalizedName == "ngSrc"))) { + return $sce.RESOURCE_URL; + } + } + + + function addAttrInterpolateDirective(node, directives, value, name) { + var interpolateFn = $interpolate(value, true); + + // no interpolation found -> ignore + if (!interpolateFn) return; + + + if (name === "multiple" && nodeName_(node) === "SELECT") { + throw $compileMinErr("selmulti", + "Binding to the 'multiple' attribute is not supported. Element: {0}", + startingTag(node)); + } + + directives.push({ + priority: 100, + compile: function() { + return { + pre: function attrInterpolatePreLinkFn(scope, element, attr) { + var $$observers = (attr.$$observers || (attr.$$observers = {})); + + if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { + throw $compileMinErr('nodomevents', + "Interpolations for HTML DOM event attributes are disallowed. Please use the " + + "ng- versions (such as ng-click instead of onclick) instead."); + } + + // we need to interpolate again, in case the attribute value has been updated + // (e.g. by another directive's compile function) + interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name)); + + // if attribute was updated so that there is no interpolation going on we don't want to + // register any observers + if (!interpolateFn) return; + + // TODO(i): this should likely be attr.$set(name, iterpolateFn(scope) so that we reset the + // actual attr value + attr[name] = interpolateFn(scope); + ($$observers[name] || ($$observers[name] = [])).$$inter = true; + (attr.$$observers && attr.$$observers[name].$$scope || scope). + $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) { + //special case for class attribute addition + removal + //so that class changes can tap into the animation + //hooks provided by the $animate service. Be sure to + //skip animations when the first digest occurs (when + //both the new and the old values are the same) since + //the CSS classes are the non-interpolated values + if(name === 'class' && newValue != oldValue) { + attr.$updateClass(newValue, oldValue); + } else { + attr.$set(name, newValue); + } + }); + } + }; + } + }); + } + + + /** + * This is a special jqLite.replaceWith, which can replace items which + * have no parents, provided that the containing jqLite collection is provided. + * + * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes + * in the root of the tree. + * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep + * the shell, but replace its DOM node reference. + * @param {Node} newNode The new DOM node. + */ + function replaceWith($rootElement, elementsToRemove, newNode) { + var firstElementToRemove = elementsToRemove[0], + removeCount = elementsToRemove.length, + parent = firstElementToRemove.parentNode, + i, ii; + + if ($rootElement) { + for(i = 0, ii = $rootElement.length; i < ii; i++) { + if ($rootElement[i] == firstElementToRemove) { + $rootElement[i++] = newNode; + for (var j = i, j2 = j + removeCount - 1, + jj = $rootElement.length; + j < jj; j++, j2++) { + if (j2 < jj) { + $rootElement[j] = $rootElement[j2]; + } else { + delete $rootElement[j]; + } + } + $rootElement.length -= removeCount - 1; + break; + } + } + } + + if (parent) { + parent.replaceChild(newNode, firstElementToRemove); + } + var fragment = document.createDocumentFragment(); + fragment.appendChild(firstElementToRemove); + newNode[jqLite.expando] = firstElementToRemove[jqLite.expando]; + for (var k = 1, kk = elementsToRemove.length; k < kk; k++) { + var element = elementsToRemove[k]; + jqLite(element).remove(); // must do this way to clean up expando + fragment.appendChild(element); + delete elementsToRemove[k]; + } + + elementsToRemove[0] = newNode; + elementsToRemove.length = 1; + } + + + function cloneAndAnnotateFn(fn, annotation) { + return extend(function() { return fn.apply(null, arguments); }, fn, annotation); + } + }]; +} + +var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; +/** + * Converts all accepted directives format into proper directive name. + * All of these will become 'myDirective': + * my:Directive + * my-directive + * x-my-directive + * data-my:directive + * + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ +function directiveNormalize(name) { + return camelCase(name.replace(PREFIX_REGEXP, '')); +} + +/** + * @ngdoc type + * @name $compile.directive.Attributes + * + * @description + * A shared object between directive compile / linking functions which contains normalized DOM + * element attributes. The values reflect current binding state `{{ }}`. The normalization is + * needed since all of these are treated as equivalent in Angular: + * + * ``` + * + * ``` + */ + +/** + * @ngdoc property + * @name $compile.directive.Attributes#$attr + * + * @description + * A map of DOM element attribute names to the normalized name. This is + * needed to do reverse lookup from normalized name back to actual name. + */ + + +/** + * @ngdoc method + * @name $compile.directive.Attributes#$set + * @kind function + * + * @description + * Set DOM element attribute value. + * + * + * @param {string} name Normalized element attribute name of the property to modify. The name is + * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr} + * property to the original name. + * @param {string} value Value to set the attribute to. The value can be an interpolated string. + */ + + + +/** + * Closure compiler type information + */ + +function nodesetLinkingFn( + /* angular.Scope */ scope, + /* NodeList */ nodeList, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +){} + +function directiveLinkingFn( + /* nodesetLinkingFn */ nodesetLinkingFn, + /* angular.Scope */ scope, + /* Node */ node, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +){} + +function tokenDifference(str1, str2) { + var values = '', + tokens1 = str1.split(/\s+/), + tokens2 = str2.split(/\s+/); + + outer: + for(var i = 0; i < tokens1.length; i++) { + var token = tokens1[i]; + for(var j = 0; j < tokens2.length; j++) { + if(token == tokens2[j]) continue outer; + } + values += (values.length > 0 ? ' ' : '') + token; + } + return values; +} + +/** + * @ngdoc provider + * @name $controllerProvider + * @description + * The {@link ng.$controller $controller service} is used by Angular to create new + * controllers. + * + * This provider allows controller registration via the + * {@link ng.$controllerProvider#register register} method. + */ +function $ControllerProvider() { + var controllers = {}, + CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/; + + + /** + * @ngdoc method + * @name $controllerProvider#register + * @param {string|Object} name Controller name, or an object map of controllers where the keys are + * the names and the values are the constructors. + * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI + * annotations in the array notation). + */ + this.register = function(name, constructor) { + assertNotHasOwnProperty(name, 'controller'); + if (isObject(name)) { + extend(controllers, name); + } else { + controllers[name] = constructor; + } + }; + + + this.$get = ['$injector', '$window', function($injector, $window) { + + /** + * @ngdoc service + * @name $controller + * @requires $injector + * + * @param {Function|string} constructor If called with a function then it's considered to be the + * controller constructor function. Otherwise it's considered to be a string which is used + * to retrieve the controller constructor using the following steps: + * + * * check if a controller with given name is registered via `$controllerProvider` + * * check if evaluating the string on the current scope returns a constructor + * * check `window[constructor]` on the global `window` object + * + * @param {Object} locals Injection locals for Controller. + * @return {Object} Instance of given controller. + * + * @description + * `$controller` service is responsible for instantiating controllers. + * + * It's just a simple call to {@link auto.$injector $injector}, but extracted into + * a service, so that one can override this service with [BC version](https://gist.github.com/1649788). + */ + return function(expression, locals) { + var instance, match, constructor, identifier; + + if(isString(expression)) { + match = expression.match(CNTRL_REG), + constructor = match[1], + identifier = match[3]; + expression = controllers.hasOwnProperty(constructor) + ? controllers[constructor] + : getter(locals.$scope, constructor, true) || getter($window, constructor, true); + + assertArgFn(expression, constructor, true); + } + + instance = $injector.instantiate(expression, locals); + + if (identifier) { + if (!(locals && typeof locals.$scope === 'object')) { + throw minErr('$controller')('noscp', + "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", + constructor || expression.name, identifier); + } + + locals.$scope[identifier] = instance; + } + + return instance; + }; + }]; +} + +/** + * @ngdoc service + * @name $document + * @requires $window + * + * @description + * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object. + * + * @example + + +
+

$document title:

+

window.document title:

+
+
+ + angular.module('documentExample', []) + .controller('ExampleController', ['$scope', '$document', function($scope, $document) { + $scope.title = $document[0].title; + $scope.windowTitle = angular.element(window.document)[0].title; + }]); + +
+ */ +function $DocumentProvider(){ + this.$get = ['$window', function(window){ + return jqLite(window.document); + }]; +} + +/** + * @ngdoc service + * @name $exceptionHandler + * @requires ng.$log + * + * @description + * Any uncaught exception in angular expressions is delegated to this service. + * The default implementation simply delegates to `$log.error` which logs it into + * the browser console. + * + * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by + * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. + * + * ## Example: + * + * ```js + * angular.module('exceptionOverride', []).factory('$exceptionHandler', function () { + * return function (exception, cause) { + * exception.message += ' (caused by "' + cause + '")'; + * throw exception; + * }; + * }); + * ``` + * + * This example will override the normal action of `$exceptionHandler`, to make angular + * exceptions fail hard when they happen, instead of just logging to the console. + * + * @param {Error} exception Exception associated with the error. + * @param {string=} cause optional information about the context in which + * the error was thrown. + * + */ +function $ExceptionHandlerProvider() { + this.$get = ['$log', function($log) { + return function(exception, cause) { + $log.error.apply($log, arguments); + }; + }]; +} + +/** + * Parse headers into key value object + * + * @param {string} headers Raw headers as a string + * @returns {Object} Parsed headers as key value object + */ +function parseHeaders(headers) { + var parsed = {}, key, val, i; + + if (!headers) return parsed; + + forEach(headers.split('\n'), function(line) { + i = line.indexOf(':'); + key = lowercase(trim(line.substr(0, i))); + val = trim(line.substr(i + 1)); + + if (key) { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + }); + + return parsed; +} + + +/** + * Returns a function that provides access to parsed headers. + * + * Headers are lazy parsed when first requested. + * @see parseHeaders + * + * @param {(string|Object)} headers Headers to provide access to. + * @returns {function(string=)} Returns a getter function which if called with: + * + * - if called with single an argument returns a single header value or null + * - if called with no arguments returns an object containing all headers. + */ +function headersGetter(headers) { + var headersObj = isObject(headers) ? headers : undefined; + + return function(name) { + if (!headersObj) headersObj = parseHeaders(headers); + + if (name) { + return headersObj[lowercase(name)] || null; + } + + return headersObj; + }; +} + + +/** + * Chain all given functions + * + * This function is used for both request and response transforming + * + * @param {*} data Data to transform. + * @param {function(string=)} headers Http headers getter fn. + * @param {(Function|Array.)} fns Function or an array of functions. + * @returns {*} Transformed data. + */ +function transformData(data, headers, fns) { + if (isFunction(fns)) + return fns(data, headers); + + forEach(fns, function(fn) { + data = fn(data, headers); + }); + + return data; +} + + +function isSuccess(status) { + return 200 <= status && status < 300; +} + + +/** + * @ngdoc provider + * @name $httpProvider + * @description + * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. + * */ +function $HttpProvider() { + var JSON_START = /^\s*(\[|\{[^\{])/, + JSON_END = /[\}\]]\s*$/, + PROTECTION_PREFIX = /^\)\]\}',?\n/, + CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'}; + + /** + * @ngdoc property + * @name $httpProvider#defaults + * @description + * + * Object containing default values for all {@link ng.$http $http} requests. + * + * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. + * Defaults value is `'XSRF-TOKEN'`. + * + * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the + * XSRF token. Defaults value is `'X-XSRF-TOKEN'`. + * + * - **`defaults.headers`** - {Object} - Default headers for all $http requests. + * Refer to {@link ng.$http#setting-http-headers $http} for documentation on + * setting default headers. + * - **`defaults.headers.common`** + * - **`defaults.headers.post`** + * - **`defaults.headers.put`** + * - **`defaults.headers.patch`** + **/ + var defaults = this.defaults = { + // transform incoming response data + transformResponse: [function(data) { + if (isString(data)) { + // strip json vulnerability protection prefix + data = data.replace(PROTECTION_PREFIX, ''); + if (JSON_START.test(data) && JSON_END.test(data)) + data = fromJson(data); + } + return data; + }], + + // transform outgoing request data + transformRequest: [function(d) { + return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d; + }], + + // default headers + headers: { + common: { + 'Accept': 'application/json, text/plain, */*' + }, + post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), + put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), + patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON) + }, + + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN' + }; + + /** + * Are ordered by request, i.e. they are applied in the same order as the + * array, on request, but reverse order, on response. + */ + var interceptorFactories = this.interceptors = []; + + /** + * For historical reasons, response interceptors are ordered by the order in which + * they are applied to the response. (This is the opposite of interceptorFactories) + */ + var responseInterceptorFactories = this.responseInterceptors = []; + + this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', + function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { + + var defaultCache = $cacheFactory('$http'); + + /** + * Interceptors stored in reverse order. Inner interceptors before outer interceptors. + * The reversal is needed so that we can build up the interception chain around the + * server request. + */ + var reversedInterceptors = []; + + forEach(interceptorFactories, function(interceptorFactory) { + reversedInterceptors.unshift(isString(interceptorFactory) + ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); + }); + + forEach(responseInterceptorFactories, function(interceptorFactory, index) { + var responseFn = isString(interceptorFactory) + ? $injector.get(interceptorFactory) + : $injector.invoke(interceptorFactory); + + /** + * Response interceptors go before "around" interceptors (no real reason, just + * had to pick one.) But they are already reversed, so we can't use unshift, hence + * the splice. + */ + reversedInterceptors.splice(index, 0, { + response: function(response) { + return responseFn($q.when(response)); + }, + responseError: function(response) { + return responseFn($q.reject(response)); + } + }); + }); + + + /** + * @ngdoc service + * @kind function + * @name $http + * @requires ng.$httpBackend + * @requires $cacheFactory + * @requires $rootScope + * @requires $q + * @requires $injector + * + * @description + * The `$http` service is a core Angular service that facilitates communication with the remote + * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest) + * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP). + * + * For unit testing applications that use `$http` service, see + * {@link ngMock.$httpBackend $httpBackend mock}. + * + * For a higher level of abstraction, please check out the {@link ngResource.$resource + * $resource} service. + * + * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by + * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage + * it is important to familiarize yourself with these APIs and the guarantees they provide. + * + * + * # General usage + * The `$http` service is a function which takes a single argument — a configuration object — + * that is used to generate an HTTP request and returns a {@link ng.$q promise} + * with two $http specific methods: `success` and `error`. + * + * ```js + * $http({method: 'GET', url: '/someUrl'}). + * success(function(data, status, headers, config) { + * // this callback will be called asynchronously + * // when the response is available + * }). + * error(function(data, status, headers, config) { + * // called asynchronously if an error occurs + * // or server returns response with an error status. + * }); + * ``` + * + * Since the returned value of calling the $http function is a `promise`, you can also use + * the `then` method to register callbacks, and these callbacks will receive a single argument – + * an object representing the response. See the API signature and type info below for more + * details. + * + * A response status code between 200 and 299 is considered a success status and + * will result in the success callback being called. Note that if the response is a redirect, + * XMLHttpRequest will transparently follow it, meaning that the error callback will not be + * called for such responses. + * + * # Writing Unit Tests that use $http + * When unit testing (using {@link ngMock ngMock}), it is necessary to call + * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending + * request using trained responses. + * + * ``` + * $httpBackend.expectGET(...); + * $http.get(...); + * $httpBackend.flush(); + * ``` + * + * # Shortcut methods + * + * Shortcut methods are also available. All shortcut methods require passing in the URL, and + * request data must be passed in for POST/PUT requests. + * + * ```js + * $http.get('/someUrl').success(successCallback); + * $http.post('/someUrl', data).success(successCallback); + * ``` + * + * Complete list of shortcut methods: + * + * - {@link ng.$http#get $http.get} + * - {@link ng.$http#head $http.head} + * - {@link ng.$http#post $http.post} + * - {@link ng.$http#put $http.put} + * - {@link ng.$http#delete $http.delete} + * - {@link ng.$http#jsonp $http.jsonp} + * - {@link ng.$http#patch $http.patch} + * + * + * # Setting HTTP Headers + * + * The $http service will automatically add certain HTTP headers to all requests. These defaults + * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration + * object, which currently contains this default configuration: + * + * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): + * - `Accept: application/json, text/plain, * / *` + * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) + * - `Content-Type: application/json` + * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) + * - `Content-Type: application/json` + * + * To add or overwrite these defaults, simply add or remove a property from these configuration + * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object + * with the lowercased HTTP method name as the key, e.g. + * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }. + * + * The defaults can also be set at runtime via the `$http.defaults` object in the same + * fashion. For example: + * + * ``` + * module.run(function($http) { + * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w' + * }); + * ``` + * + * In addition, you can supply a `headers` property in the config object passed when + * calling `$http(config)`, which overrides the defaults without changing them globally. + * + * + * # Transforming Requests and Responses + * + * Both requests and responses can be transformed using transform functions. By default, Angular + * applies these transformations: + * + * Request transformations: + * + * - If the `data` property of the request configuration object contains an object, serialize it + * into JSON format. + * + * Response transformations: + * + * - If XSRF prefix is detected, strip it (see Security Considerations section below). + * - If JSON response is detected, deserialize it using a JSON parser. + * + * To globally augment or override the default transforms, modify the + * `$httpProvider.defaults.transformRequest` and `$httpProvider.defaults.transformResponse` + * properties. These properties are by default an array of transform functions, which allows you + * to `push` or `unshift` a new transformation function into the transformation chain. You can + * also decide to completely override any default transformations by assigning your + * transformation functions to these properties directly without the array wrapper. These defaults + * are again available on the $http factory at run-time, which may be useful if you have run-time + * services you wish to be involved in your transformations. + * + * Similarly, to locally override the request/response transforms, augment the + * `transformRequest` and/or `transformResponse` properties of the configuration object passed + * into `$http`. + * + * + * # Caching + * + * To enable caching, set the request configuration `cache` property to `true` (to use default + * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}). + * When the cache is enabled, `$http` stores the response from the server in the specified + * cache. The next time the same request is made, the response is served from the cache without + * sending a request to the server. + * + * Note that even if the response is served from cache, delivery of the data is asynchronous in + * the same way that real requests are. + * + * If there are multiple GET requests for the same URL that should be cached using the same + * cache, but the cache is not populated yet, only one request to the server will be made and + * the remaining requests will be fulfilled using the response from the first request. + * + * You can change the default cache to a new object (built with + * {@link ng.$cacheFactory `$cacheFactory`}) by updating the + * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set + * their `cache` property to `true` will now use this cache object. + * + * If you set the default cache to `false` then only requests that specify their own custom + * cache object will be cached. + * + * # Interceptors + * + * Before you start creating interceptors, be sure to understand the + * {@link ng.$q $q and deferred/promise APIs}. + * + * For purposes of global error handling, authentication, or any kind of synchronous or + * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be + * able to intercept requests before they are handed to the server and + * responses before they are handed over to the application code that + * initiated these requests. The interceptors leverage the {@link ng.$q + * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. + * + * The interceptors are service factories that are registered with the `$httpProvider` by + * adding them to the `$httpProvider.interceptors` array. The factory is called and + * injected with dependencies (if specified) and returns the interceptor. + * + * There are two kinds of interceptors (and two kinds of rejection interceptors): + * + * * `request`: interceptors get called with a http `config` object. The function is free to + * modify the `config` object or create a new one. The function needs to return the `config` + * object directly, or a promise containing the `config` or a new `config` object. + * * `requestError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. + * * `response`: interceptors get called with http `response` object. The function is free to + * modify the `response` object or create a new one. The function needs to return the `response` + * object directly, or as a promise containing the `response` or a new `response` object. + * * `responseError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. + * + * + * ```js + * // register the interceptor as a service + * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { + * return { + * // optional method + * 'request': function(config) { + * // do something on success + * return config; + * }, + * + * // optional method + * 'requestError': function(rejection) { + * // do something on error + * if (canRecover(rejection)) { + * return responseOrNewPromise + * } + * return $q.reject(rejection); + * }, + * + * + * + * // optional method + * 'response': function(response) { + * // do something on success + * return response; + * }, + * + * // optional method + * 'responseError': function(rejection) { + * // do something on error + * if (canRecover(rejection)) { + * return responseOrNewPromise + * } + * return $q.reject(rejection); + * } + * }; + * }); + * + * $httpProvider.interceptors.push('myHttpInterceptor'); + * + * + * // alternatively, register the interceptor via an anonymous factory + * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { + * return { + * 'request': function(config) { + * // same as above + * }, + * + * 'response': function(response) { + * // same as above + * } + * }; + * }); + * ``` + * + * # Response interceptors (DEPRECATED) + * + * Before you start creating interceptors, be sure to understand the + * {@link ng.$q $q and deferred/promise APIs}. + * + * For purposes of global error handling, authentication or any kind of synchronous or + * asynchronous preprocessing of received responses, it is desirable to be able to intercept + * responses for http requests before they are handed over to the application code that + * initiated these requests. The response interceptors leverage the {@link ng.$q + * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing. + * + * The interceptors are service factories that are registered with the $httpProvider by + * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and + * injected with dependencies (if specified) and returns the interceptor — a function that + * takes a {@link ng.$q promise} and returns the original or a new promise. + * + * ```js + * // register the interceptor as a service + * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { + * return function(promise) { + * return promise.then(function(response) { + * // do something on success + * return response; + * }, function(response) { + * // do something on error + * if (canRecover(response)) { + * return responseOrNewPromise + * } + * return $q.reject(response); + * }); + * } + * }); + * + * $httpProvider.responseInterceptors.push('myHttpInterceptor'); + * + * + * // register the interceptor via an anonymous factory + * $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) { + * return function(promise) { + * // same as above + * } + * }); + * ``` + * + * + * # Security Considerations + * + * When designing web applications, consider security threats from: + * + * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) + * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) + * + * Both server and the client must cooperate in order to eliminate these threats. Angular comes + * pre-configured with strategies that address these issues, but for this to work backend server + * cooperation is required. + * + * ## JSON Vulnerability Protection + * + * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) + * allows third party website to turn your JSON resource URL into + * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To + * counter this your server can prefix all JSON requests with following string `")]}',\n"`. + * Angular will automatically strip the prefix before processing it as JSON. + * + * For example if your server needs to return: + * ```js + * ['one','two'] + * ``` + * + * which is vulnerable to attack, your server can return: + * ```js + * )]}', + * ['one','two'] + * ``` + * + * Angular will strip the prefix, before processing the JSON. + * + * + * ## Cross Site Request Forgery (XSRF) Protection + * + * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which + * an unauthorized site can gain your user's private data. Angular provides a mechanism + * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie + * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only + * JavaScript that runs on your domain could read the cookie, your server can be assured that + * the XHR came from JavaScript running on your domain. The header will not be set for + * cross-domain requests. + * + * To take advantage of this, your server needs to set a token in a JavaScript readable session + * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the + * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure + * that only JavaScript running on your domain could have sent the request. The token must be + * unique for each user and must be verifiable by the server (to prevent the JavaScript from + * making up its own tokens). We recommend that the token is a digest of your site's + * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) + * for added security. + * + * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName + * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, + * or the per-request config object. + * + * + * @param {object} config Object describing the request to be made and how it should be + * processed. The object has following properties: + * + * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) + * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. + * - **params** – `{Object.}` – Map of strings or objects which will be turned + * to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be + * JSONified. + * - **data** – `{string|Object}` – Data to be sent as the request message data. + * - **headers** – `{Object}` – Map of strings or functions which return strings representing + * HTTP headers to send to the server. If the return value of a function is null, the + * header will not be sent. + * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. + * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. + * - **transformRequest** – + * `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * request body and headers and returns its transformed (typically serialized) version. + * - **transformResponse** – + * `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * response body and headers and returns its transformed (typically deserialized) version. + * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the + * GET request, otherwise if a cache instance built with + * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for + * caching. + * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} + * that should abort the request when resolved. + * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the + * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) + * for more information. + * - **responseType** - `{string}` - see + * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType). + * + * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the + * standard `then` method and two http specific methods: `success` and `error`. The `then` + * method takes two arguments a success and an error callback which will be called with a + * response object. The `success` and `error` methods take a single argument - a function that + * will be called when the request succeeds or fails respectively. The arguments passed into + * these functions are destructured representation of the response object passed into the + * `then` method. The response object has these properties: + * + * - **data** – `{string|Object}` – The response body transformed with the transform + * functions. + * - **status** – `{number}` – HTTP status code of the response. + * - **headers** – `{function([headerName])}` – Header getter function. + * - **config** – `{Object}` – The configuration object that was used to generate the request. + * - **statusText** – `{string}` – HTTP status text of the response. + * + * @property {Array.} pendingRequests Array of config objects for currently pending + * requests. This is primarily meant to be used for debugging purposes. + * + * + * @example + + +
+ + +
+ + + +
http status code: {{status}}
+
http response data: {{data}}
+
+
+ + angular.module('httpExample', []) + .controller('FetchController', ['$scope', '$http', '$templateCache', + function($scope, $http, $templateCache) { + $scope.method = 'GET'; + $scope.url = 'http-hello.html'; + + $scope.fetch = function() { + $scope.code = null; + $scope.response = null; + + $http({method: $scope.method, url: $scope.url, cache: $templateCache}). + success(function(data, status) { + $scope.status = status; + $scope.data = data; + }). + error(function(data, status) { + $scope.data = data || "Request failed"; + $scope.status = status; + }); + }; + + $scope.updateModel = function(method, url) { + $scope.method = method; + $scope.url = url; + }; + }]); + + + Hello, $http! + + + var status = element(by.binding('status')); + var data = element(by.binding('data')); + var fetchBtn = element(by.id('fetchbtn')); + var sampleGetBtn = element(by.id('samplegetbtn')); + var sampleJsonpBtn = element(by.id('samplejsonpbtn')); + var invalidJsonpBtn = element(by.id('invalidjsonpbtn')); + + it('should make an xhr GET request', function() { + sampleGetBtn.click(); + fetchBtn.click(); + expect(status.getText()).toMatch('200'); + expect(data.getText()).toMatch(/Hello, \$http!/); + }); + + it('should make a JSONP request to angularjs.org', function() { + sampleJsonpBtn.click(); + fetchBtn.click(); + expect(status.getText()).toMatch('200'); + expect(data.getText()).toMatch(/Super Hero!/); + }); + + it('should make JSONP request to invalid URL and invoke the error handler', + function() { + invalidJsonpBtn.click(); + fetchBtn.click(); + expect(status.getText()).toMatch('0'); + expect(data.getText()).toMatch('Request failed'); + }); + +
+ */ + function $http(requestConfig) { + var config = { + method: 'get', + transformRequest: defaults.transformRequest, + transformResponse: defaults.transformResponse + }; + var headers = mergeHeaders(requestConfig); + + extend(config, requestConfig); + config.headers = headers; + config.method = uppercase(config.method); + + var serverRequest = function(config) { + headers = config.headers; + var reqData = transformData(config.data, headersGetter(headers), config.transformRequest); + + // strip content-type if data is undefined + if (isUndefined(reqData)) { + forEach(headers, function(value, header) { + if (lowercase(header) === 'content-type') { + delete headers[header]; + } + }); + } + + if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { + config.withCredentials = defaults.withCredentials; + } + + // send request + return sendReq(config, reqData, headers).then(transformResponse, transformResponse); + }; + + var chain = [serverRequest, undefined]; + var promise = $q.when(config); + + // apply interceptors + forEach(reversedInterceptors, function(interceptor) { + if (interceptor.request || interceptor.requestError) { + chain.unshift(interceptor.request, interceptor.requestError); + } + if (interceptor.response || interceptor.responseError) { + chain.push(interceptor.response, interceptor.responseError); + } + }); + + while(chain.length) { + var thenFn = chain.shift(); + var rejectFn = chain.shift(); + + promise = promise.then(thenFn, rejectFn); + } + + promise.success = function(fn) { + promise.then(function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + promise.error = function(fn) { + promise.then(null, function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + return promise; + + function transformResponse(response) { + // make a copy since the response must be cacheable + var resp = extend({}, response, { + data: transformData(response.data, response.headers, config.transformResponse) + }); + return (isSuccess(response.status)) + ? resp + : $q.reject(resp); + } + + function mergeHeaders(config) { + var defHeaders = defaults.headers, + reqHeaders = extend({}, config.headers), + defHeaderName, lowercaseDefHeaderName, reqHeaderName; + + defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); + + // using for-in instead of forEach to avoid unecessary iteration after header has been found + defaultHeadersIteration: + for (defHeaderName in defHeaders) { + lowercaseDefHeaderName = lowercase(defHeaderName); + + for (reqHeaderName in reqHeaders) { + if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { + continue defaultHeadersIteration; + } + } + + reqHeaders[defHeaderName] = defHeaders[defHeaderName]; + } + + // execute if header value is a function for merged headers + execHeaders(reqHeaders); + return reqHeaders; + + function execHeaders(headers) { + var headerContent; + + forEach(headers, function(headerFn, header) { + if (isFunction(headerFn)) { + headerContent = headerFn(); + if (headerContent != null) { + headers[header] = headerContent; + } else { + delete headers[header]; + } + } + }); + } + } + } + + $http.pendingRequests = []; + + /** + * @ngdoc method + * @name $http#get + * + * @description + * Shortcut method to perform `GET` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#delete + * + * @description + * Shortcut method to perform `DELETE` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#head + * + * @description + * Shortcut method to perform `HEAD` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#jsonp + * + * @description + * Shortcut method to perform `JSONP` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request. + * The name of the callback should be the string `JSON_CALLBACK`. + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethods('get', 'delete', 'head', 'jsonp'); + + /** + * @ngdoc method + * @name $http#post + * + * @description + * Shortcut method to perform `POST` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#put + * + * @description + * Shortcut method to perform `PUT` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethodsWithData('post', 'put'); + + /** + * @ngdoc property + * @name $http#defaults + * + * @description + * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of + * default headers, withCredentials as well as request and response transformations. + * + * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. + */ + $http.defaults = defaults; + + + return $http; + + + function createShortMethods(names) { + forEach(arguments, function(name) { + $http[name] = function(url, config) { + return $http(extend(config || {}, { + method: name, + url: url + })); + }; + }); + } + + + function createShortMethodsWithData(name) { + forEach(arguments, function(name) { + $http[name] = function(url, data, config) { + return $http(extend(config || {}, { + method: name, + url: url, + data: data + })); + }; + }); + } + + + /** + * Makes the request. + * + * !!! ACCESSES CLOSURE VARS: + * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests + */ + function sendReq(config, reqData, reqHeaders) { + var deferred = $q.defer(), + promise = deferred.promise, + cache, + cachedResp, + url = buildUrl(config.url, config.params); + + $http.pendingRequests.push(config); + promise.then(removePendingReq, removePendingReq); + + + if ((config.cache || defaults.cache) && config.cache !== false && + (config.method === 'GET' || config.method === 'JSONP')) { + cache = isObject(config.cache) ? config.cache + : isObject(defaults.cache) ? defaults.cache + : defaultCache; + } + + if (cache) { + cachedResp = cache.get(url); + if (isDefined(cachedResp)) { + if (isPromiseLike(cachedResp)) { + // cached request has already been sent, but there is no response yet + cachedResp.then(removePendingReq, removePendingReq); + return cachedResp; + } else { + // serving from cache + if (isArray(cachedResp)) { + resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); + } else { + resolvePromise(cachedResp, 200, {}, 'OK'); + } + } + } else { + // put the promise for the non-transformed response into cache as a placeholder + cache.put(url, promise); + } + } + + + // if we won't have the response in cache, set the xsrf headers and + // send the request to the backend + if (isUndefined(cachedResp)) { + var xsrfValue = urlIsSameOrigin(config.url) + ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName] + : undefined; + if (xsrfValue) { + reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; + } + + $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, + config.withCredentials, config.responseType); + } + + return promise; + + + /** + * Callback registered to $httpBackend(): + * - caches the response if desired + * - resolves the raw $http promise + * - calls $apply + */ + function done(status, response, headersString, statusText) { + if (cache) { + if (isSuccess(status)) { + cache.put(url, [status, response, parseHeaders(headersString), statusText]); + } else { + // remove promise from the cache + cache.remove(url); + } + } + + resolvePromise(response, status, headersString, statusText); + if (!$rootScope.$$phase) $rootScope.$apply(); + } + + + /** + * Resolves the raw $http promise. + */ + function resolvePromise(response, status, headers, statusText) { + // normalize internal statuses to 0 + status = Math.max(status, 0); + + (isSuccess(status) ? deferred.resolve : deferred.reject)({ + data: response, + status: status, + headers: headersGetter(headers), + config: config, + statusText : statusText + }); + } + + + function removePendingReq() { + var idx = indexOf($http.pendingRequests, config); + if (idx !== -1) $http.pendingRequests.splice(idx, 1); + } + } + + + function buildUrl(url, params) { + if (!params) return url; + var parts = []; + forEachSorted(params, function(value, key) { + if (value === null || isUndefined(value)) return; + if (!isArray(value)) value = [value]; + + forEach(value, function(v) { + if (isObject(v)) { + if (isDate(v)){ + v = v.toISOString(); + } else { + v = toJson(v); + } + } + parts.push(encodeUriQuery(key) + '=' + + encodeUriQuery(v)); + }); + }); + if(parts.length > 0) { + url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); + } + return url; + } + }]; +} + +function createXhr(method) { + //if IE and the method is not RFC2616 compliant, or if XMLHttpRequest + //is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest + //if it is available + if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) || + !window.XMLHttpRequest)) { + return new window.ActiveXObject("Microsoft.XMLHTTP"); + } else if (window.XMLHttpRequest) { + return new window.XMLHttpRequest(); + } + + throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest."); +} + +/** + * @ngdoc service + * @name $httpBackend + * @requires $window + * @requires $document + * + * @description + * HTTP backend used by the {@link ng.$http service} that delegates to + * XMLHttpRequest object or JSONP and deals with browser incompatibilities. + * + * You should never need to use this service directly, instead use the higher-level abstractions: + * {@link ng.$http $http} or {@link ngResource.$resource $resource}. + * + * During testing this implementation is swapped with {@link ngMock.$httpBackend mock + * $httpBackend} which can be trained with responses. + */ +function $HttpBackendProvider() { + this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { + return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]); + }]; +} + +function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { + var ABORTED = -1; + + // TODO(vojta): fix the signature + return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { + var status; + $browser.$$incOutstandingRequestCount(); + url = url || $browser.url(); + + if (lowercase(method) == 'jsonp') { + var callbackId = '_' + (callbacks.counter++).toString(36); + callbacks[callbackId] = function(data) { + callbacks[callbackId].data = data; + callbacks[callbackId].called = true; + }; + + var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), + callbackId, function(status, text) { + completeRequest(callback, status, callbacks[callbackId].data, "", text); + callbacks[callbackId] = noop; + }); + } else { + + var xhr = createXhr(method); + + xhr.open(method, url, true); + forEach(headers, function(value, key) { + if (isDefined(value)) { + xhr.setRequestHeader(key, value); + } + }); + + // In IE6 and 7, this might be called synchronously when xhr.send below is called and the + // response is in the cache. the promise api will ensure that to the app code the api is + // always async + xhr.onreadystatechange = function() { + // onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by + // xhrs that are resolved while the app is in the background (see #5426). + // since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before + // continuing + // + // we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and + // Safari respectively. + if (xhr && xhr.readyState == 4) { + var responseHeaders = null, + response = null, + statusText = ''; + + if(status !== ABORTED) { + responseHeaders = xhr.getAllResponseHeaders(); + + // responseText is the old-school way of retrieving response (supported by IE8 & 9) + // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) + response = ('response' in xhr) ? xhr.response : xhr.responseText; + } + + // Accessing statusText on an aborted xhr object will + // throw an 'c00c023f error' in IE9 and lower, don't touch it. + if (!(status === ABORTED && msie < 10)) { + statusText = xhr.statusText; + } + + completeRequest(callback, + status || xhr.status, + response, + responseHeaders, + statusText); + } + }; + + if (withCredentials) { + xhr.withCredentials = true; + } + + if (responseType) { + try { + xhr.responseType = responseType; + } catch (e) { + // WebKit added support for the json responseType value on 09/03/2013 + // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are + // known to throw when setting the value "json" as the response type. Other older + // browsers implementing the responseType + // + // The json response type can be ignored if not supported, because JSON payloads are + // parsed on the client-side regardless. + if (responseType !== 'json') { + throw e; + } + } + } + + xhr.send(post || null); + } + + if (timeout > 0) { + var timeoutId = $browserDefer(timeoutRequest, timeout); + } else if (isPromiseLike(timeout)) { + timeout.then(timeoutRequest); + } + + + function timeoutRequest() { + status = ABORTED; + jsonpDone && jsonpDone(); + xhr && xhr.abort(); + } + + function completeRequest(callback, status, response, headersString, statusText) { + // cancel timeout and subsequent timeout promise resolution + timeoutId && $browserDefer.cancel(timeoutId); + jsonpDone = xhr = null; + + // fix status code when it is 0 (0 status is undocumented). + // Occurs when accessing file resources or on Android 4.1 stock browser + // while retrieving files from application cache. + if (status === 0) { + status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0; + } + + // normalize IE bug (http://bugs.jquery.com/ticket/1450) + status = status === 1223 ? 204 : status; + statusText = statusText || ''; + + callback(status, response, headersString, statusText); + $browser.$$completeOutstandingRequest(noop); + } + }; + + function jsonpReq(url, callbackId, done) { + // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: + // - fetches local scripts via XHR and evals them + // - adds and immediately removes script elements from the document + var script = rawDocument.createElement('script'), callback = null; + script.type = "text/javascript"; + script.src = url; + script.async = true; + + callback = function(event) { + removeEventListenerFn(script, "load", callback); + removeEventListenerFn(script, "error", callback); + rawDocument.body.removeChild(script); + script = null; + var status = -1; + var text = "unknown"; + + if (event) { + if (event.type === "load" && !callbacks[callbackId].called) { + event = { type: "error" }; + } + text = event.type; + status = event.type === "error" ? 404 : 200; + } + + if (done) { + done(status, text); + } + }; + + addEventListenerFn(script, "load", callback); + addEventListenerFn(script, "error", callback); + + if (msie <= 8) { + script.onreadystatechange = function() { + if (isString(script.readyState) && /loaded|complete/.test(script.readyState)) { + script.onreadystatechange = null; + callback({ + type: 'load' + }); + } + }; + } + + rawDocument.body.appendChild(script); + return callback; + } +} + +var $interpolateMinErr = minErr('$interpolate'); + +/** + * @ngdoc provider + * @name $interpolateProvider + * @kind function + * + * @description + * + * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. + * + * @example + + + +
+ //demo.label// +
+
+ + it('should interpolate binding with custom symbols', function() { + expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.'); + }); + +
+ */ +function $InterpolateProvider() { + var startSymbol = '{{'; + var endSymbol = '}}'; + + /** + * @ngdoc method + * @name $interpolateProvider#startSymbol + * @description + * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. + * + * @param {string=} value new value to set the starting symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.startSymbol = function(value){ + if (value) { + startSymbol = value; + return this; + } else { + return startSymbol; + } + }; + + /** + * @ngdoc method + * @name $interpolateProvider#endSymbol + * @description + * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. + * + * @param {string=} value new value to set the ending symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.endSymbol = function(value){ + if (value) { + endSymbol = value; + return this; + } else { + return endSymbol; + } + }; + + + this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) { + var startSymbolLength = startSymbol.length, + endSymbolLength = endSymbol.length; + + /** + * @ngdoc service + * @name $interpolate + * @kind function + * + * @requires $parse + * @requires $sce + * + * @description + * + * Compiles a string with markup into an interpolation function. This service is used by the + * HTML {@link ng.$compile $compile} service for data binding. See + * {@link ng.$interpolateProvider $interpolateProvider} for configuring the + * interpolation markup. + * + * + * ```js + * var $interpolate = ...; // injected + * var exp = $interpolate('Hello {{name | uppercase}}!'); + * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!'); + * ``` + * + * + * @param {string} text The text with markup to interpolate. + * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have + * embedded expression in order to return an interpolation function. Strings with no + * embedded expression will return null for the interpolation function. + * @param {string=} trustedContext when provided, the returned function passes the interpolated + * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, + * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that + * provides Strict Contextual Escaping for details. + * @returns {function(context)} an interpolation function which is used to compute the + * interpolated string. The function has these parameters: + * + * * `context`: an object against which any expressions embedded in the strings are evaluated + * against. + * + */ + function $interpolate(text, mustHaveExpression, trustedContext) { + var startIndex, + endIndex, + index = 0, + parts = [], + length = text.length, + hasInterpolation = false, + fn, + exp, + concat = []; + + while(index < length) { + if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) && + ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) { + (index != startIndex) && parts.push(text.substring(index, startIndex)); + parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex))); + fn.exp = exp; + index = endIndex + endSymbolLength; + hasInterpolation = true; + } else { + // we did not find anything, so we have to add the remainder to the parts array + (index != length) && parts.push(text.substring(index)); + index = length; + } + } + + if (!(length = parts.length)) { + // we added, nothing, must have been an empty string. + parts.push(''); + length = 1; + } + + // Concatenating expressions makes it hard to reason about whether some combination of + // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a + // single expression be used for iframe[src], object[src], etc., we ensure that the value + // that's used is assigned or constructed by some JS code somewhere that is more testable or + // make it obvious that you bound the value to some user controlled value. This helps reduce + // the load when auditing for XSS issues. + if (trustedContext && parts.length > 1) { + throw $interpolateMinErr('noconcat', + "Error while interpolating: {0}\nStrict Contextual Escaping disallows " + + "interpolations that concatenate multiple expressions when a trusted value is " + + "required. See http://docs.angularjs.org/api/ng.$sce", text); + } + + if (!mustHaveExpression || hasInterpolation) { + concat.length = length; + fn = function(context) { + try { + for(var i = 0, ii = length, part; i + * **Note**: Intervals created by this service must be explicitly destroyed when you are finished + * with them. In particular they are not automatically destroyed when a controller's scope or a + * directive's element are destroyed. + * You should take this into consideration and make sure to always cancel the interval at the + * appropriate moment. See the example below for more details on how and when to do this. + * + * + * @param {function()} fn A function that should be called repeatedly. + * @param {number} delay Number of milliseconds between each function call. + * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat + * indefinitely. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @returns {promise} A promise which will be notified on each iteration. + * + * @example + * + * + * + * + *
+ *
+ * Date format:
+ * Current time is: + *
+ * Blood 1 : {{blood_1}} + * Blood 2 : {{blood_2}} + * + * + * + *
+ *
+ * + *
+ *
+ */ + function interval(fn, delay, count, invokeApply) { + var setInterval = $window.setInterval, + clearInterval = $window.clearInterval, + deferred = $q.defer(), + promise = deferred.promise, + iteration = 0, + skipApply = (isDefined(invokeApply) && !invokeApply); + + count = isDefined(count) ? count : 0; + + promise.then(null, null, fn); + + promise.$$intervalId = setInterval(function tick() { + deferred.notify(iteration++); + + if (count > 0 && iteration >= count) { + deferred.resolve(iteration); + clearInterval(promise.$$intervalId); + delete intervals[promise.$$intervalId]; + } + + if (!skipApply) $rootScope.$apply(); + + }, delay); + + intervals[promise.$$intervalId] = deferred; + + return promise; + } + + + /** + * @ngdoc method + * @name $interval#cancel + * + * @description + * Cancels a task associated with the `promise`. + * + * @param {promise} promise returned by the `$interval` function. + * @returns {boolean} Returns `true` if the task was successfully canceled. + */ + interval.cancel = function(promise) { + if (promise && promise.$$intervalId in intervals) { + intervals[promise.$$intervalId].reject('canceled'); + $window.clearInterval(promise.$$intervalId); + delete intervals[promise.$$intervalId]; + return true; + } + return false; + }; + + return interval; + }]; +} + +/** + * @ngdoc service + * @name $locale + * + * @description + * $locale service provides localization rules for various Angular components. As of right now the + * only public api is: + * + * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) + */ +function $LocaleProvider(){ + this.$get = function() { + return { + id: 'en-us', + + NUMBER_FORMATS: { + DECIMAL_SEP: '.', + GROUP_SEP: ',', + PATTERNS: [ + { // Decimal Pattern + minInt: 1, + minFrac: 0, + maxFrac: 3, + posPre: '', + posSuf: '', + negPre: '-', + negSuf: '', + gSize: 3, + lgSize: 3 + },{ //Currency Pattern + minInt: 1, + minFrac: 2, + maxFrac: 2, + posPre: '\u00A4', + posSuf: '', + negPre: '(\u00A4', + negSuf: ')', + gSize: 3, + lgSize: 3 + } + ], + CURRENCY_SYM: '$' + }, + + DATETIME_FORMATS: { + MONTH: + 'January,February,March,April,May,June,July,August,September,October,November,December' + .split(','), + SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), + DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), + SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), + AMPMS: ['AM','PM'], + medium: 'MMM d, y h:mm:ss a', + short: 'M/d/yy h:mm a', + fullDate: 'EEEE, MMMM d, y', + longDate: 'MMMM d, y', + mediumDate: 'MMM d, y', + shortDate: 'M/d/yy', + mediumTime: 'h:mm:ss a', + shortTime: 'h:mm a' + }, + + pluralCat: function(num) { + if (num === 1) { + return 'one'; + } + return 'other'; + } + }; + }; +} + +var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/, + DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; +var $locationMinErr = minErr('$location'); + + +/** + * Encode path using encodeUriSegment, ignoring forward slashes + * + * @param {string} path Path to encode + * @returns {string} + */ +function encodePath(path) { + var segments = path.split('/'), + i = segments.length; + + while (i--) { + segments[i] = encodeUriSegment(segments[i]); + } + + return segments.join('/'); +} + +function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) { + var parsedUrl = urlResolve(absoluteUrl, appBase); + + locationObj.$$protocol = parsedUrl.protocol; + locationObj.$$host = parsedUrl.hostname; + locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; +} + + +function parseAppUrl(relativeUrl, locationObj, appBase) { + var prefixed = (relativeUrl.charAt(0) !== '/'); + if (prefixed) { + relativeUrl = '/' + relativeUrl; + } + var match = urlResolve(relativeUrl, appBase); + locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? + match.pathname.substring(1) : match.pathname); + locationObj.$$search = parseKeyValue(match.search); + locationObj.$$hash = decodeURIComponent(match.hash); + + // make sure path starts with '/'; + if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') { + locationObj.$$path = '/' + locationObj.$$path; + } +} + + +/** + * + * @param {string} begin + * @param {string} whole + * @returns {string} returns text from whole after begin or undefined if it does not begin with + * expected string. + */ +function beginsWith(begin, whole) { + if (whole.indexOf(begin) === 0) { + return whole.substr(begin.length); + } +} + + +function stripHash(url) { + var index = url.indexOf('#'); + return index == -1 ? url : url.substr(0, index); +} + + +function stripFile(url) { + return url.substr(0, stripHash(url).lastIndexOf('/') + 1); +} + +/* return the server only (scheme://host:port) */ +function serverBase(url) { + return url.substring(0, url.indexOf('/', url.indexOf('//') + 2)); +} + + +/** + * LocationHtml5Url represents an url + * This object is exposed as $location service when HTML5 mode is enabled and supported + * + * @constructor + * @param {string} appBase application base URL + * @param {string} basePrefix url path prefix + */ +function LocationHtml5Url(appBase, basePrefix) { + this.$$html5 = true; + basePrefix = basePrefix || ''; + var appBaseNoFile = stripFile(appBase); + parseAbsoluteUrl(appBase, this, appBase); + + + /** + * Parse given html5 (regular) url string into properties + * @param {string} newAbsoluteUrl HTML5 url + * @private + */ + this.$$parse = function(url) { + var pathUrl = beginsWith(appBaseNoFile, url); + if (!isString(pathUrl)) { + throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, + appBaseNoFile); + } + + parseAppUrl(pathUrl, this, appBase); + + if (!this.$$path) { + this.$$path = '/'; + } + + this.$$compose(); + }; + + /** + * Compose url and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' + }; + + this.$$rewrite = function(url) { + var appUrl, prevAppUrl; + + if ( (appUrl = beginsWith(appBase, url)) !== undefined ) { + prevAppUrl = appUrl; + if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) { + return appBaseNoFile + (beginsWith('/', appUrl) || appUrl); + } else { + return appBase + prevAppUrl; + } + } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) { + return appBaseNoFile + appUrl; + } else if (appBaseNoFile == url + '/') { + return appBaseNoFile; + } + }; +} + + +/** + * LocationHashbangUrl represents url + * This object is exposed as $location service when developer doesn't opt into html5 mode. + * It also serves as the base class for html5 mode fallback on legacy browsers. + * + * @constructor + * @param {string} appBase application base URL + * @param {string} hashPrefix hashbang prefix + */ +function LocationHashbangUrl(appBase, hashPrefix) { + var appBaseNoFile = stripFile(appBase); + + parseAbsoluteUrl(appBase, this, appBase); + + + /** + * Parse given hashbang url into properties + * @param {string} url Hashbang url + * @private + */ + this.$$parse = function(url) { + var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url); + var withoutHashUrl = withoutBaseUrl.charAt(0) == '#' + ? beginsWith(hashPrefix, withoutBaseUrl) + : (this.$$html5) + ? withoutBaseUrl + : ''; + + if (!isString(withoutHashUrl)) { + throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url, + hashPrefix); + } + parseAppUrl(withoutHashUrl, this, appBase); + + this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); + + this.$$compose(); + + /* + * In Windows, on an anchor node on documents loaded from + * the filesystem, the browser will return a pathname + * prefixed with the drive name ('/C:/path') when a + * pathname without a drive is set: + * * a.setAttribute('href', '/foo') + * * a.pathname === '/C:/foo' //true + * + * Inside of Angular, we're always using pathnames that + * do not include drive names for routing. + */ + function removeWindowsDriveName (path, url, base) { + /* + Matches paths for file protocol on windows, + such as /C:/foo/bar, and captures only /foo/bar. + */ + var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; + + var firstPathSegmentMatch; + + //Get the relative path from the input URL. + if (url.indexOf(base) === 0) { + url = url.replace(base, ''); + } + + // The input URL intentionally contains a first path segment that ends with a colon. + if (windowsFilePathExp.exec(url)) { + return path; + } + + firstPathSegmentMatch = windowsFilePathExp.exec(path); + return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; + } + }; + + /** + * Compose hashbang url and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); + }; + + this.$$rewrite = function(url) { + if(stripHash(appBase) == stripHash(url)) { + return url; + } + }; +} + + +/** + * LocationHashbangUrl represents url + * This object is exposed as $location service when html5 history api is enabled but the browser + * does not support it. + * + * @constructor + * @param {string} appBase application base URL + * @param {string} hashPrefix hashbang prefix + */ +function LocationHashbangInHtml5Url(appBase, hashPrefix) { + this.$$html5 = true; + LocationHashbangUrl.apply(this, arguments); + + var appBaseNoFile = stripFile(appBase); + + this.$$rewrite = function(url) { + var appUrl; + + if ( appBase == stripHash(url) ) { + return url; + } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) { + return appBase + hashPrefix + appUrl; + } else if ( appBaseNoFile === url + '/') { + return appBaseNoFile; + } + }; + + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#' + this.$$absUrl = appBase + hashPrefix + this.$$url; + }; + +} + + +LocationHashbangInHtml5Url.prototype = + LocationHashbangUrl.prototype = + LocationHtml5Url.prototype = { + + /** + * Are we in html5 mode? + * @private + */ + $$html5: false, + + /** + * Has any change been replacing ? + * @private + */ + $$replace: false, + + /** + * @ngdoc method + * @name $location#absUrl + * + * @description + * This method is getter only. + * + * Return full url representation with all segments encoded according to rules specified in + * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). + * + * @return {string} full url + */ + absUrl: locationGetter('$$absUrl'), + + /** + * @ngdoc method + * @name $location#url + * + * @description + * This method is getter / setter. + * + * Return url (e.g. `/path?a=b#hash`) when called without any parameter. + * + * Change path, search and hash, when called with parameter and return `$location`. + * + * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) + * @return {string} url + */ + url: function(url) { + if (isUndefined(url)) + return this.$$url; + + var match = PATH_MATCH.exec(url); + if (match[1]) this.path(decodeURIComponent(match[1])); + if (match[2] || match[1]) this.search(match[3] || ''); + this.hash(match[5] || ''); + + return this; + }, + + /** + * @ngdoc method + * @name $location#protocol + * + * @description + * This method is getter only. + * + * Return protocol of current url. + * + * @return {string} protocol of current url + */ + protocol: locationGetter('$$protocol'), + + /** + * @ngdoc method + * @name $location#host + * + * @description + * This method is getter only. + * + * Return host of current url. + * + * @return {string} host of current url. + */ + host: locationGetter('$$host'), + + /** + * @ngdoc method + * @name $location#port + * + * @description + * This method is getter only. + * + * Return port of current url. + * + * @return {Number} port + */ + port: locationGetter('$$port'), + + /** + * @ngdoc method + * @name $location#path + * + * @description + * This method is getter / setter. + * + * Return path of current url when called without any parameter. + * + * Change path when called with parameter and return `$location`. + * + * Note: Path should always begin with forward slash (/), this method will add the forward slash + * if it is missing. + * + * @param {(string|number)=} path New path + * @return {string} path + */ + path: locationGetterSetter('$$path', function(path) { + path = path ? path.toString() : ''; + return path.charAt(0) == '/' ? path : '/' + path; + }), + + /** + * @ngdoc method + * @name $location#search + * + * @description + * This method is getter / setter. + * + * Return search part (as object) of current url when called without any parameter. + * + * Change search part when called with parameter and return `$location`. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var searchObject = $location.search(); + * // => {foo: 'bar', baz: 'xoxo'} + * + * + * // set foo to 'yipee' + * $location.search('foo', 'yipee'); + * // => $location + * ``` + * + * @param {string|Object.|Object.>} search New search params - string or + * hash object. + * + * When called with a single argument the method acts as a setter, setting the `search` component + * of `$location` to the specified value. + * + * If the argument is a hash object containing an array of values, these values will be encoded + * as duplicate search parameters in the url. + * + * @param {(string|Number|Array|boolean)=} paramValue If `search` is a string or number, then `paramValue` + * will override only a single search property. + * + * If `paramValue` is an array, it will override the property of the `search` component of + * `$location` specified via the first argument. + * + * If `paramValue` is `null`, the property specified via the first argument will be deleted. + * + * If `paramValue` is `true`, the property specified via the first argument will be added with no + * value nor trailing equal sign. + * + * @return {Object} If called with no arguments returns the parsed `search` object. If called with + * one or more arguments returns `$location` object itself. + */ + search: function(search, paramValue) { + switch (arguments.length) { + case 0: + return this.$$search; + case 1: + if (isString(search) || isNumber(search)) { + search = search.toString(); + this.$$search = parseKeyValue(search); + } else if (isObject(search)) { + // remove object undefined or null properties + forEach(search, function(value, key) { + if (value == null) delete search[key]; + }); + + this.$$search = search; + } else { + throw $locationMinErr('isrcharg', + 'The first argument of the `$location#search()` call must be a string or an object.'); + } + break; + default: + if (isUndefined(paramValue) || paramValue === null) { + delete this.$$search[search]; + } else { + this.$$search[search] = paramValue; + } + } + + this.$$compose(); + return this; + }, + + /** + * @ngdoc method + * @name $location#hash + * + * @description + * This method is getter / setter. + * + * Return hash fragment when called without any parameter. + * + * Change hash fragment when called with parameter and return `$location`. + * + * @param {(string|number)=} hash New hash fragment + * @return {string} hash + */ + hash: locationGetterSetter('$$hash', function(hash) { + return hash ? hash.toString() : ''; + }), + + /** + * @ngdoc method + * @name $location#replace + * + * @description + * If called, all changes to $location during current `$digest` will be replacing current history + * record, instead of adding new one. + */ + replace: function() { + this.$$replace = true; + return this; + } +}; + +function locationGetter(property) { + return function() { + return this[property]; + }; +} + + +function locationGetterSetter(property, preprocess) { + return function(value) { + if (isUndefined(value)) + return this[property]; + + this[property] = preprocess(value); + this.$$compose(); + + return this; + }; +} + + +/** + * @ngdoc service + * @name $location + * + * @requires $rootElement + * + * @description + * The $location service parses the URL in the browser address bar (based on the + * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL + * available to your application. Changes to the URL in the address bar are reflected into + * $location service and changes to $location are reflected into the browser address bar. + * + * **The $location service:** + * + * - Exposes the current URL in the browser address bar, so you can + * - Watch and observe the URL. + * - Change the URL. + * - Synchronizes the URL with the browser when the user + * - Changes the address bar. + * - Clicks the back or forward button (or clicks a History link). + * - Clicks on a link. + * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). + * + * For more information see {@link guide/$location Developer Guide: Using $location} + */ + +/** + * @ngdoc provider + * @name $locationProvider + * @description + * Use the `$locationProvider` to configure how the application deep linking paths are stored. + */ +function $LocationProvider(){ + var hashPrefix = '', + html5Mode = false; + + /** + * @ngdoc method + * @name $locationProvider#hashPrefix + * @description + * @param {string=} prefix Prefix for hash part (containing path and search) + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.hashPrefix = function(prefix) { + if (isDefined(prefix)) { + hashPrefix = prefix; + return this; + } else { + return hashPrefix; + } + }; + + /** + * @ngdoc method + * @name $locationProvider#html5Mode + * @description + * @param {boolean=} mode Use HTML5 strategy if available. + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.html5Mode = function(mode) { + if (isDefined(mode)) { + html5Mode = mode; + return this; + } else { + return html5Mode; + } + }; + + /** + * @ngdoc event + * @name $location#$locationChangeStart + * @eventType broadcast on root scope + * @description + * Broadcasted before a URL will change. This change can be prevented by calling + * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more + * details about event object. Upon successful change + * {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + */ + + /** + * @ngdoc event + * @name $location#$locationChangeSuccess + * @eventType broadcast on root scope + * @description + * Broadcasted after a URL was changed. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + */ + + this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', + function( $rootScope, $browser, $sniffer, $rootElement) { + var $location, + LocationMode, + baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' + initialUrl = $browser.url(), + appBase; + + if (html5Mode) { + appBase = serverBase(initialUrl) + (baseHref || '/'); + LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; + } else { + appBase = stripHash(initialUrl); + LocationMode = LocationHashbangUrl; + } + $location = new LocationMode(appBase, '#' + hashPrefix); + $location.$$parse($location.$$rewrite(initialUrl)); + + var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; + + $rootElement.on('click', function(event) { + // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) + // currently we open nice url link and redirect then + + if (event.ctrlKey || event.metaKey || event.which == 2) return; + + var elm = jqLite(event.target); + + // traverse the DOM up to find first A tag + while (lowercase(elm[0].nodeName) !== 'a') { + // ignore rewriting if no A tag (reached root element, or no parent - removed from document) + if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; + } + + var absHref = elm.prop('href'); + + if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { + // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during + // an animation. + absHref = urlResolve(absHref.animVal).href; + } + + // Ignore when url is started with javascript: or mailto: + if (IGNORE_URI_REGEXP.test(absHref)) return; + + // Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9) + // The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or + // somewhere#anchor or http://example.com/somewhere + if (LocationMode === LocationHashbangInHtml5Url) { + // get the actual href attribute - see + // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx + var href = elm.attr('href') || elm.attr('xlink:href'); + + if (href && href.indexOf('://') < 0) { // Ignore absolute URLs + var prefix = '#' + hashPrefix; + if (href[0] == '/') { + // absolute path - replace old path + absHref = appBase + prefix + href; + } else if (href[0] == '#') { + // local anchor + absHref = appBase + prefix + ($location.path() || '/') + href; + } else { + // relative path - join with current path + var stack = $location.path().split("/"), + parts = href.split("/"); + if (stack.length === 2 && !stack[1]) stack.length = 1; + for (var i=0; i html5 url + if ($location.absUrl() != initialUrl) { + $browser.url($location.absUrl(), true); + } + + // update $location when $browser url changes + $browser.onUrlChange(function(newUrl) { + if ($location.absUrl() != newUrl) { + $rootScope.$evalAsync(function() { + var oldUrl = $location.absUrl(); + + $location.$$parse(newUrl); + if ($rootScope.$broadcast('$locationChangeStart', newUrl, + oldUrl).defaultPrevented) { + $location.$$parse(oldUrl); + $browser.url(oldUrl); + } else { + afterLocationChange(oldUrl); + } + }); + if (!$rootScope.$$phase) $rootScope.$digest(); + } + }); + + // update browser + var changeCounter = 0; + $rootScope.$watch(function $locationWatch() { + var oldUrl = $browser.url(); + var currentReplace = $location.$$replace; + + if (!changeCounter || oldUrl != $location.absUrl()) { + changeCounter++; + $rootScope.$evalAsync(function() { + if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). + defaultPrevented) { + $location.$$parse(oldUrl); + } else { + $browser.url($location.absUrl(), currentReplace); + afterLocationChange(oldUrl); + } + }); + } + $location.$$replace = false; + + return changeCounter; + }); + + return $location; + + function afterLocationChange(oldUrl) { + $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); + } +}]; +} + +/** + * @ngdoc service + * @name $log + * @requires $window + * + * @description + * Simple service for logging. Default implementation safely writes the message + * into the browser's console (if present). + * + * The main purpose of this service is to simplify debugging and troubleshooting. + * + * The default is to log `debug` messages. You can use + * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this. + * + * @example + + + angular.module('logExample', []) + .controller('LogController', ['$scope', '$log', function($scope, $log) { + $scope.$log = $log; + $scope.message = 'Hello World!'; + }]); + + +
+

Reload this page with open console, enter text and hit the log button...

+ Message: + + + + + +
+
+
+ */ + +/** + * @ngdoc provider + * @name $logProvider + * @description + * Use the `$logProvider` to configure how the application logs messages + */ +function $LogProvider(){ + var debug = true, + self = this; + + /** + * @ngdoc method + * @name $logProvider#debugEnabled + * @description + * @param {boolean=} flag enable or disable debug level messages + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.debugEnabled = function(flag) { + if (isDefined(flag)) { + debug = flag; + return this; + } else { + return debug; + } + }; + + this.$get = ['$window', function($window){ + return { + /** + * @ngdoc method + * @name $log#log + * + * @description + * Write a log message + */ + log: consoleLog('log'), + + /** + * @ngdoc method + * @name $log#info + * + * @description + * Write an information message + */ + info: consoleLog('info'), + + /** + * @ngdoc method + * @name $log#warn + * + * @description + * Write a warning message + */ + warn: consoleLog('warn'), + + /** + * @ngdoc method + * @name $log#error + * + * @description + * Write an error message + */ + error: consoleLog('error'), + + /** + * @ngdoc method + * @name $log#debug + * + * @description + * Write a debug message + */ + debug: (function () { + var fn = consoleLog('debug'); + + return function() { + if (debug) { + fn.apply(self, arguments); + } + }; + }()) + }; + + function formatError(arg) { + if (arg instanceof Error) { + if (arg.stack) { + arg = (arg.message && arg.stack.indexOf(arg.message) === -1) + ? 'Error: ' + arg.message + '\n' + arg.stack + : arg.stack; + } else if (arg.sourceURL) { + arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; + } + } + return arg; + } + + function consoleLog(type) { + var console = $window.console || {}, + logFn = console[type] || console.log || noop, + hasApply = false; + + // Note: reading logFn.apply throws an error in IE11 in IE8 document mode. + // The reason behind this is that console.log has type "object" in IE8... + try { + hasApply = !!logFn.apply; + } catch (e) {} + + if (hasApply) { + return function() { + var args = []; + forEach(arguments, function(arg) { + args.push(formatError(arg)); + }); + return logFn.apply(console, args); + }; + } + + // we are IE which either doesn't have window.console => this is noop and we do nothing, + // or we are IE where console.log doesn't have apply so we log at least first 2 args + return function(arg1, arg2) { + logFn(arg1, arg2 == null ? '' : arg2); + }; + } + }]; +} + +var $parseMinErr = minErr('$parse'); +var promiseWarningCache = {}; +var promiseWarning; + +// Sandboxing Angular Expressions +// ------------------------------ +// Angular expressions are generally considered safe because these expressions only have direct +// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by +// obtaining a reference to native JS functions such as the Function constructor. +// +// As an example, consider the following Angular expression: +// +// {}.toString.constructor('alert("evil JS code")') +// +// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits +// against the expression language, but not to prevent exploits that were enabled by exposing +// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good +// practice and therefore we are not even trying to protect against interaction with an object +// explicitly exposed in this way. +// +// In general, it is not possible to access a Window object from an angular expression unless a +// window or some DOM object that has a reference to window is published onto a Scope. +// Similarly we prevent invocations of function known to be dangerous, as well as assignments to +// native objects. + + +function ensureSafeMemberName(name, fullExpression) { + if (name === "__defineGetter__" || name === "__defineSetter__" + || name === "__lookupGetter__" || name === "__lookupSetter__" + || name === "__proto__") { + throw $parseMinErr('isecfld', + 'Attempting to access a disallowed field in Angular expressions! ' + +'Expression: {0}', fullExpression); + } + return name; +} + +function ensureSafeObject(obj, fullExpression) { + // nifty check if obj is Function that is fast and works across iframes and other contexts + if (obj) { + if (obj.constructor === obj) { + throw $parseMinErr('isecfn', + 'Referencing Function in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (// isWindow(obj) + obj.document && obj.location && obj.alert && obj.setInterval) { + throw $parseMinErr('isecwindow', + 'Referencing the Window in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (// isElement(obj) + obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) { + throw $parseMinErr('isecdom', + 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (// block Object so that we can't get hold of dangerous Object.* methods + obj === Object) { + throw $parseMinErr('isecobj', + 'Referencing Object in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } + } + return obj; +} + +var CALL = Function.prototype.call; +var APPLY = Function.prototype.apply; +var BIND = Function.prototype.bind; + +function ensureSafeFunction(obj, fullExpression) { + if (obj) { + if (obj.constructor === obj) { + throw $parseMinErr('isecfn', + 'Referencing Function in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (obj === CALL || obj === APPLY || (BIND && obj === BIND)) { + throw $parseMinErr('isecff', + 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } + } +} + +var OPERATORS = { + /* jshint bitwise : false */ + 'null':function(){return null;}, + 'true':function(){return true;}, + 'false':function(){return false;}, + undefined:noop, + '+':function(self, locals, a,b){ + a=a(self, locals); b=b(self, locals); + if (isDefined(a)) { + if (isDefined(b)) { + return a + b; + } + return a; + } + return isDefined(b)?b:undefined;}, + '-':function(self, locals, a,b){ + a=a(self, locals); b=b(self, locals); + return (isDefined(a)?a:0)-(isDefined(b)?b:0); + }, + '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, + '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, + '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, + '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, + '=':noop, + '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);}, + '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);}, + '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);}, + '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);}, + '<':function(self, locals, a,b){return a(self, locals)':function(self, locals, a,b){return a(self, locals)>b(self, locals);}, + '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);}, + '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);}, + '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);}, + '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);}, + '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, +// '|':function(self, locals, a,b){return a|b;}, + '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));}, + '!':function(self, locals, a){return !a(self, locals);} +}; +/* jshint bitwise: true */ +var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; + + +///////////////////////////////////////// + + +/** + * @constructor + */ +var Lexer = function (options) { + this.options = options; +}; + +Lexer.prototype = { + constructor: Lexer, + + lex: function (text) { + this.text = text; + + this.index = 0; + this.ch = undefined; + this.lastCh = ':'; // can start regexp + + this.tokens = []; + + while (this.index < this.text.length) { + this.ch = this.text.charAt(this.index); + if (this.is('"\'')) { + this.readString(this.ch); + } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) { + this.readNumber(); + } else if (this.isIdent(this.ch)) { + this.readIdent(); + } else if (this.is('(){}[].,;:?')) { + this.tokens.push({ + index: this.index, + text: this.ch + }); + this.index++; + } else if (this.isWhitespace(this.ch)) { + this.index++; + continue; + } else { + var ch2 = this.ch + this.peek(); + var ch3 = ch2 + this.peek(2); + var fn = OPERATORS[this.ch]; + var fn2 = OPERATORS[ch2]; + var fn3 = OPERATORS[ch3]; + if (fn3) { + this.tokens.push({index: this.index, text: ch3, fn: fn3}); + this.index += 3; + } else if (fn2) { + this.tokens.push({index: this.index, text: ch2, fn: fn2}); + this.index += 2; + } else if (fn) { + this.tokens.push({ + index: this.index, + text: this.ch, + fn: fn + }); + this.index += 1; + } else { + this.throwError('Unexpected next character ', this.index, this.index + 1); + } + } + this.lastCh = this.ch; + } + return this.tokens; + }, + + is: function(chars) { + return chars.indexOf(this.ch) !== -1; + }, + + was: function(chars) { + return chars.indexOf(this.lastCh) !== -1; + }, + + peek: function(i) { + var num = i || 1; + return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; + }, + + isNumber: function(ch) { + return ('0' <= ch && ch <= '9'); + }, + + isWhitespace: function(ch) { + // IE treats non-breaking space as \u00A0 + return (ch === ' ' || ch === '\r' || ch === '\t' || + ch === '\n' || ch === '\v' || ch === '\u00A0'); + }, + + isIdent: function(ch) { + return ('a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' === ch || ch === '$'); + }, + + isExpOperator: function(ch) { + return (ch === '-' || ch === '+' || this.isNumber(ch)); + }, + + throwError: function(error, start, end) { + end = end || this.index; + var colStr = (isDefined(start) + ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']' + : ' ' + end); + throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].', + error, colStr, this.text); + }, + + readNumber: function() { + var number = ''; + var start = this.index; + while (this.index < this.text.length) { + var ch = lowercase(this.text.charAt(this.index)); + if (ch == '.' || this.isNumber(ch)) { + number += ch; + } else { + var peekCh = this.peek(); + if (ch == 'e' && this.isExpOperator(peekCh)) { + number += ch; + } else if (this.isExpOperator(ch) && + peekCh && this.isNumber(peekCh) && + number.charAt(number.length - 1) == 'e') { + number += ch; + } else if (this.isExpOperator(ch) && + (!peekCh || !this.isNumber(peekCh)) && + number.charAt(number.length - 1) == 'e') { + this.throwError('Invalid exponent'); + } else { + break; + } + } + this.index++; + } + number = 1 * number; + this.tokens.push({ + index: start, + text: number, + literal: true, + constant: true, + fn: function() { return number; } + }); + }, + + readIdent: function() { + var parser = this; + + var ident = ''; + var start = this.index; + + var lastDot, peekIndex, methodName, ch; + + while (this.index < this.text.length) { + ch = this.text.charAt(this.index); + if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) { + if (ch === '.') lastDot = this.index; + ident += ch; + } else { + break; + } + this.index++; + } + + //check if this is not a method invocation and if it is back out to last dot + if (lastDot) { + peekIndex = this.index; + while (peekIndex < this.text.length) { + ch = this.text.charAt(peekIndex); + if (ch === '(') { + methodName = ident.substr(lastDot - start + 1); + ident = ident.substr(0, lastDot - start); + this.index = peekIndex; + break; + } + if (this.isWhitespace(ch)) { + peekIndex++; + } else { + break; + } + } + } + + + var token = { + index: start, + text: ident + }; + + // OPERATORS is our own object so we don't need to use special hasOwnPropertyFn + if (OPERATORS.hasOwnProperty(ident)) { + token.fn = OPERATORS[ident]; + token.literal = true; + token.constant = true; + } else { + var getter = getterFn(ident, this.options, this.text); + token.fn = extend(function(self, locals) { + return (getter(self, locals)); + }, { + assign: function(self, value) { + return setter(self, ident, value, parser.text, parser.options); + } + }); + } + + this.tokens.push(token); + + if (methodName) { + this.tokens.push({ + index:lastDot, + text: '.' + }); + this.tokens.push({ + index: lastDot + 1, + text: methodName + }); + } + }, + + readString: function(quote) { + var start = this.index; + this.index++; + var string = ''; + var rawString = quote; + var escape = false; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + rawString += ch; + if (escape) { + if (ch === 'u') { + var hex = this.text.substring(this.index + 1, this.index + 5); + if (!hex.match(/[\da-f]{4}/i)) + this.throwError('Invalid unicode escape [\\u' + hex + ']'); + this.index += 4; + string += String.fromCharCode(parseInt(hex, 16)); + } else { + var rep = ESCAPE[ch]; + string = string + (rep || ch); + } + escape = false; + } else if (ch === '\\') { + escape = true; + } else if (ch === quote) { + this.index++; + this.tokens.push({ + index: start, + text: rawString, + string: string, + literal: true, + constant: true, + fn: function() { return string; } + }); + return; + } else { + string += ch; + } + this.index++; + } + this.throwError('Unterminated quote', start); + } +}; + + +/** + * @constructor + */ +var Parser = function (lexer, $filter, options) { + this.lexer = lexer; + this.$filter = $filter; + this.options = options; +}; + +Parser.ZERO = extend(function () { + return 0; +}, { + constant: true +}); + +Parser.prototype = { + constructor: Parser, + + parse: function (text) { + this.text = text; + + this.tokens = this.lexer.lex(text); + + var value = this.statements(); + + if (this.tokens.length !== 0) { + this.throwError('is an unexpected token', this.tokens[0]); + } + + value.literal = !!value.literal; + value.constant = !!value.constant; + + return value; + }, + + primary: function () { + var primary; + if (this.expect('(')) { + primary = this.filterChain(); + this.consume(')'); + } else if (this.expect('[')) { + primary = this.arrayDeclaration(); + } else if (this.expect('{')) { + primary = this.object(); + } else { + var token = this.expect(); + primary = token.fn; + if (!primary) { + this.throwError('not a primary expression', token); + } + primary.literal = !!token.literal; + primary.constant = !!token.constant; + } + + var next, context; + while ((next = this.expect('(', '[', '.'))) { + if (next.text === '(') { + primary = this.functionCall(primary, context); + context = null; + } else if (next.text === '[') { + context = primary; + primary = this.objectIndex(primary); + } else if (next.text === '.') { + context = primary; + primary = this.fieldAccess(primary); + } else { + this.throwError('IMPOSSIBLE'); + } + } + return primary; + }, + + throwError: function(msg, token) { + throw $parseMinErr('syntax', + 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].', + token.text, msg, (token.index + 1), this.text, this.text.substring(token.index)); + }, + + peekToken: function() { + if (this.tokens.length === 0) + throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); + return this.tokens[0]; + }, + + peek: function(e1, e2, e3, e4) { + if (this.tokens.length > 0) { + var token = this.tokens[0]; + var t = token.text; + if (t === e1 || t === e2 || t === e3 || t === e4 || + (!e1 && !e2 && !e3 && !e4)) { + return token; + } + } + return false; + }, + + expect: function(e1, e2, e3, e4){ + var token = this.peek(e1, e2, e3, e4); + if (token) { + this.tokens.shift(); + return token; + } + return false; + }, + + consume: function(e1){ + if (!this.expect(e1)) { + this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); + } + }, + + unaryFn: function(fn, right) { + return extend(function(self, locals) { + return fn(self, locals, right); + }, { + constant:right.constant + }); + }, + + ternaryFn: function(left, middle, right){ + return extend(function(self, locals){ + return left(self, locals) ? middle(self, locals) : right(self, locals); + }, { + constant: left.constant && middle.constant && right.constant + }); + }, + + binaryFn: function(left, fn, right) { + return extend(function(self, locals) { + return fn(self, locals, left, right); + }, { + constant:left.constant && right.constant + }); + }, + + statements: function() { + var statements = []; + while (true) { + if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) + statements.push(this.filterChain()); + if (!this.expect(';')) { + // optimize for the common case where there is only one statement. + // TODO(size): maybe we should not support multiple statements? + return (statements.length === 1) + ? statements[0] + : function(self, locals) { + var value; + for (var i = 0; i < statements.length; i++) { + var statement = statements[i]; + if (statement) { + value = statement(self, locals); + } + } + return value; + }; + } + } + }, + + filterChain: function() { + var left = this.expression(); + var token; + while (true) { + if ((token = this.expect('|'))) { + left = this.binaryFn(left, token.fn, this.filter()); + } else { + return left; + } + } + }, + + filter: function() { + var token = this.expect(); + var fn = this.$filter(token.text); + var argsFn = []; + while (true) { + if ((token = this.expect(':'))) { + argsFn.push(this.expression()); + } else { + var fnInvoke = function(self, locals, input) { + var args = [input]; + for (var i = 0; i < argsFn.length; i++) { + args.push(argsFn[i](self, locals)); + } + return fn.apply(self, args); + }; + return function() { + return fnInvoke; + }; + } + } + }, + + expression: function() { + return this.assignment(); + }, + + assignment: function() { + var left = this.ternary(); + var right; + var token; + if ((token = this.expect('='))) { + if (!left.assign) { + this.throwError('implies assignment but [' + + this.text.substring(0, token.index) + '] can not be assigned to', token); + } + right = this.ternary(); + return function(scope, locals) { + return left.assign(scope, right(scope, locals), locals); + }; + } + return left; + }, + + ternary: function() { + var left = this.logicalOR(); + var middle; + var token; + if ((token = this.expect('?'))) { + middle = this.assignment(); + if ((token = this.expect(':'))) { + return this.ternaryFn(left, middle, this.assignment()); + } else { + this.throwError('expected :', token); + } + } else { + return left; + } + }, + + logicalOR: function() { + var left = this.logicalAND(); + var token; + while (true) { + if ((token = this.expect('||'))) { + left = this.binaryFn(left, token.fn, this.logicalAND()); + } else { + return left; + } + } + }, + + logicalAND: function() { + var left = this.equality(); + var token; + if ((token = this.expect('&&'))) { + left = this.binaryFn(left, token.fn, this.logicalAND()); + } + return left; + }, + + equality: function() { + var left = this.relational(); + var token; + if ((token = this.expect('==','!=','===','!=='))) { + left = this.binaryFn(left, token.fn, this.equality()); + } + return left; + }, + + relational: function() { + var left = this.additive(); + var token; + if ((token = this.expect('<', '>', '<=', '>='))) { + left = this.binaryFn(left, token.fn, this.relational()); + } + return left; + }, + + additive: function() { + var left = this.multiplicative(); + var token; + while ((token = this.expect('+','-'))) { + left = this.binaryFn(left, token.fn, this.multiplicative()); + } + return left; + }, + + multiplicative: function() { + var left = this.unary(); + var token; + while ((token = this.expect('*','/','%'))) { + left = this.binaryFn(left, token.fn, this.unary()); + } + return left; + }, + + unary: function() { + var token; + if (this.expect('+')) { + return this.primary(); + } else if ((token = this.expect('-'))) { + return this.binaryFn(Parser.ZERO, token.fn, this.unary()); + } else if ((token = this.expect('!'))) { + return this.unaryFn(token.fn, this.unary()); + } else { + return this.primary(); + } + }, + + fieldAccess: function(object) { + var parser = this; + var field = this.expect().text; + var getter = getterFn(field, this.options, this.text); + + return extend(function(scope, locals, self) { + return getter(self || object(scope, locals)); + }, { + assign: function(scope, value, locals) { + var o = object(scope, locals); + if (!o) object.assign(scope, o = {}); + return setter(o, field, value, parser.text, parser.options); + } + }); + }, + + objectIndex: function(obj) { + var parser = this; + + var indexFn = this.expression(); + this.consume(']'); + + return extend(function(self, locals) { + var o = obj(self, locals), + i = indexFn(self, locals), + v, p; + + ensureSafeMemberName(i, parser.text); + if (!o) return undefined; + v = ensureSafeObject(o[i], parser.text); + if (v && v.then && parser.options.unwrapPromises) { + p = v; + if (!('$$v' in v)) { + p.$$v = undefined; + p.then(function(val) { p.$$v = val; }); + } + v = v.$$v; + } + return v; + }, { + assign: function(self, value, locals) { + var key = ensureSafeMemberName(indexFn(self, locals), parser.text); + // prevent overwriting of Function.constructor which would break ensureSafeObject check + var o = ensureSafeObject(obj(self, locals), parser.text); + if (!o) obj.assign(self, o = {}); + return o[key] = value; + } + }); + }, + + functionCall: function(fn, contextGetter) { + var argsFn = []; + if (this.peekToken().text !== ')') { + do { + argsFn.push(this.expression()); + } while (this.expect(',')); + } + this.consume(')'); + + var parser = this; + + return function(scope, locals) { + var args = []; + var context = contextGetter ? contextGetter(scope, locals) : scope; + + for (var i = 0; i < argsFn.length; i++) { + args.push(ensureSafeObject(argsFn[i](scope, locals), parser.text)); + } + var fnPtr = fn(scope, locals, context) || noop; + + ensureSafeObject(context, parser.text); + ensureSafeFunction(fnPtr, parser.text); + + // IE stupidity! (IE doesn't have apply for some native functions) + var v = fnPtr.apply + ? fnPtr.apply(context, args) + : fnPtr(args[0], args[1], args[2], args[3], args[4]); + + return ensureSafeObject(v, parser.text); + }; + }, + + // This is used with json array declaration + arrayDeclaration: function () { + var elementFns = []; + var allConstant = true; + if (this.peekToken().text !== ']') { + do { + if (this.peek(']')) { + // Support trailing commas per ES5.1. + break; + } + var elementFn = this.expression(); + elementFns.push(elementFn); + if (!elementFn.constant) { + allConstant = false; + } + } while (this.expect(',')); + } + this.consume(']'); + + return extend(function(self, locals) { + var array = []; + for (var i = 0; i < elementFns.length; i++) { + array.push(elementFns[i](self, locals)); + } + return array; + }, { + literal: true, + constant: allConstant + }); + }, + + object: function () { + var keyValues = []; + var allConstant = true; + if (this.peekToken().text !== '}') { + do { + if (this.peek('}')) { + // Support trailing commas per ES5.1. + break; + } + var token = this.expect(), + key = token.string || token.text; + this.consume(':'); + var value = this.expression(); + keyValues.push({key: key, value: value}); + if (!value.constant) { + allConstant = false; + } + } while (this.expect(',')); + } + this.consume('}'); + + return extend(function(self, locals) { + var object = {}; + for (var i = 0; i < keyValues.length; i++) { + var keyValue = keyValues[i]; + object[keyValue.key] = keyValue.value(self, locals); + } + return object; + }, { + literal: true, + constant: allConstant + }); + } +}; + + +////////////////////////////////////////////////// +// Parser helper functions +////////////////////////////////////////////////// + +function setter(obj, path, setValue, fullExp, options) { + ensureSafeObject(obj, fullExp); + + //needed? + options = options || {}; + + var element = path.split('.'), key; + for (var i = 0; element.length > 1; i++) { + key = ensureSafeMemberName(element.shift(), fullExp); + var propertyObj = ensureSafeObject(obj[key], fullExp); + if (!propertyObj) { + propertyObj = {}; + obj[key] = propertyObj; + } + obj = propertyObj; + if (obj.then && options.unwrapPromises) { + promiseWarning(fullExp); + if (!("$$v" in obj)) { + (function(promise) { + promise.then(function(val) { promise.$$v = val; }); } + )(obj); + } + if (obj.$$v === undefined) { + obj.$$v = {}; + } + obj = obj.$$v; + } + } + key = ensureSafeMemberName(element.shift(), fullExp); + ensureSafeObject(obj[key], fullExp); + obj[key] = setValue; + return setValue; +} + +var getterFnCache = {}; + +/** + * Implementation of the "Black Hole" variant from: + * - http://jsperf.com/angularjs-parse-getter/4 + * - http://jsperf.com/path-evaluation-simplified/7 + */ +function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) { + ensureSafeMemberName(key0, fullExp); + ensureSafeMemberName(key1, fullExp); + ensureSafeMemberName(key2, fullExp); + ensureSafeMemberName(key3, fullExp); + ensureSafeMemberName(key4, fullExp); + + return !options.unwrapPromises + ? function cspSafeGetter(scope, locals) { + var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope; + + if (pathVal == null) return pathVal; + pathVal = pathVal[key0]; + + if (!key1) return pathVal; + if (pathVal == null) return undefined; + pathVal = pathVal[key1]; + + if (!key2) return pathVal; + if (pathVal == null) return undefined; + pathVal = pathVal[key2]; + + if (!key3) return pathVal; + if (pathVal == null) return undefined; + pathVal = pathVal[key3]; + + if (!key4) return pathVal; + if (pathVal == null) return undefined; + pathVal = pathVal[key4]; + + return pathVal; + } + : function cspSafePromiseEnabledGetter(scope, locals) { + var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope, + promise; + + if (pathVal == null) return pathVal; + + pathVal = pathVal[key0]; + if (pathVal && pathVal.then) { + promiseWarning(fullExp); + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + + if (!key1) return pathVal; + if (pathVal == null) return undefined; + pathVal = pathVal[key1]; + if (pathVal && pathVal.then) { + promiseWarning(fullExp); + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + + if (!key2) return pathVal; + if (pathVal == null) return undefined; + pathVal = pathVal[key2]; + if (pathVal && pathVal.then) { + promiseWarning(fullExp); + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + + if (!key3) return pathVal; + if (pathVal == null) return undefined; + pathVal = pathVal[key3]; + if (pathVal && pathVal.then) { + promiseWarning(fullExp); + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + + if (!key4) return pathVal; + if (pathVal == null) return undefined; + pathVal = pathVal[key4]; + if (pathVal && pathVal.then) { + promiseWarning(fullExp); + if (!("$$v" in pathVal)) { + promise = pathVal; + promise.$$v = undefined; + promise.then(function(val) { promise.$$v = val; }); + } + pathVal = pathVal.$$v; + } + return pathVal; + }; +} + +function getterFn(path, options, fullExp) { + // Check whether the cache has this getter already. + // We can use hasOwnProperty directly on the cache because we ensure, + // see below, that the cache never stores a path called 'hasOwnProperty' + if (getterFnCache.hasOwnProperty(path)) { + return getterFnCache[path]; + } + + var pathKeys = path.split('.'), + pathKeysLength = pathKeys.length, + fn; + + // http://jsperf.com/angularjs-parse-getter/6 + if (options.csp) { + if (pathKeysLength < 6) { + fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, + options); + } else { + fn = function(scope, locals) { + var i = 0, val; + do { + val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], + pathKeys[i++], fullExp, options)(scope, locals); + + locals = undefined; // clear after first iteration + scope = val; + } while (i < pathKeysLength); + return val; + }; + } + } else { + var code = 'var p;\n'; + forEach(pathKeys, function(key, index) { + ensureSafeMemberName(key, fullExp); + code += 'if(s == null) return undefined;\n' + + 's='+ (index + // we simply dereference 's' on any .dot notation + ? 's' + // but if we are first then we check locals first, and if so read it first + : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' + + (options.unwrapPromises + ? 'if (s && s.then) {\n' + + ' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' + + ' if (!("$$v" in s)) {\n' + + ' p=s;\n' + + ' p.$$v = undefined;\n' + + ' p.then(function(v) {p.$$v=v;});\n' + + '}\n' + + ' s=s.$$v\n' + + '}\n' + : ''); + }); + code += 'return s;'; + + /* jshint -W054 */ + var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning + /* jshint +W054 */ + evaledFnGetter.toString = valueFn(code); + fn = options.unwrapPromises ? function(scope, locals) { + return evaledFnGetter(scope, locals, promiseWarning); + } : evaledFnGetter; + } + + // Only cache the value if it's not going to mess up the cache object + // This is more performant that using Object.prototype.hasOwnProperty.call + if (path !== 'hasOwnProperty') { + getterFnCache[path] = fn; + } + return fn; +} + +/////////////////////////////////// + +/** + * @ngdoc service + * @name $parse + * @kind function + * + * @description + * + * Converts Angular {@link guide/expression expression} into a function. + * + * ```js + * var getter = $parse('user.name'); + * var setter = getter.assign; + * var context = {user:{name:'angular'}}; + * var locals = {user:{name:'local'}}; + * + * expect(getter(context)).toEqual('angular'); + * setter(context, 'newValue'); + * expect(context.user.name).toEqual('newValue'); + * expect(getter(context, locals)).toEqual('local'); + * ``` + * + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + * + * The returned function also has the following properties: + * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript + * literal. + * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript + * constant literals. + * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be + * set to a function to change its value on the given context. + * + */ + + +/** + * @ngdoc provider + * @name $parseProvider + * @kind function + * + * @description + * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} + * service. + */ +function $ParseProvider() { + var cache = {}; + + var $parseOptions = { + csp: false, + unwrapPromises: false, + logPromiseWarnings: true + }; + + + /** + * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future. + * + * @ngdoc method + * @name $parseProvider#unwrapPromises + * @description + * + * **This feature is deprecated, see deprecation notes below for more info** + * + * If set to true (default is false), $parse will unwrap promises automatically when a promise is + * found at any part of the expression. In other words, if set to true, the expression will always + * result in a non-promise value. + * + * While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled, + * the fulfillment value is used in place of the promise while evaluating the expression. + * + * **Deprecation notice** + * + * This is a feature that didn't prove to be wildly useful or popular, primarily because of the + * dichotomy between data access in templates (accessed as raw values) and controller code + * (accessed as promises). + * + * In most code we ended up resolving promises manually in controllers anyway and thus unifying + * the model access there. + * + * Other downsides of automatic promise unwrapping: + * + * - when building components it's often desirable to receive the raw promises + * - adds complexity and slows down expression evaluation + * - makes expression code pre-generation unattractive due to the amount of code that needs to be + * generated + * - makes IDE auto-completion and tool support hard + * + * **Warning Logs** + * + * If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a + * promise (to reduce the noise, each expression is logged only once). To disable this logging use + * `$parseProvider.logPromiseWarnings(false)` api. + * + * + * @param {boolean=} value New value. + * @returns {boolean|self} Returns the current setting when used as getter and self if used as + * setter. + */ + this.unwrapPromises = function(value) { + if (isDefined(value)) { + $parseOptions.unwrapPromises = !!value; + return this; + } else { + return $parseOptions.unwrapPromises; + } + }; + + + /** + * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future. + * + * @ngdoc method + * @name $parseProvider#logPromiseWarnings + * @description + * + * Controls whether Angular should log a warning on any encounter of a promise in an expression. + * + * The default is set to `true`. + * + * This setting applies only if `$parseProvider.unwrapPromises` setting is set to true as well. + * + * @param {boolean=} value New value. + * @returns {boolean|self} Returns the current setting when used as getter and self if used as + * setter. + */ + this.logPromiseWarnings = function(value) { + if (isDefined(value)) { + $parseOptions.logPromiseWarnings = value; + return this; + } else { + return $parseOptions.logPromiseWarnings; + } + }; + + + this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) { + $parseOptions.csp = $sniffer.csp; + + promiseWarning = function promiseWarningFn(fullExp) { + if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return; + promiseWarningCache[fullExp] = true; + $log.warn('[$parse] Promise found in the expression `' + fullExp + '`. ' + + 'Automatic unwrapping of promises in Angular expressions is deprecated.'); + }; + + return function(exp) { + var parsedExpression; + + switch (typeof exp) { + case 'string': + + if (cache.hasOwnProperty(exp)) { + return cache[exp]; + } + + var lexer = new Lexer($parseOptions); + var parser = new Parser(lexer, $filter, $parseOptions); + parsedExpression = parser.parse(exp); + + if (exp !== 'hasOwnProperty') { + // Only cache the value if it's not going to mess up the cache object + // This is more performant that using Object.prototype.hasOwnProperty.call + cache[exp] = parsedExpression; + } + + return parsedExpression; + + case 'function': + return exp; + + default: + return noop; + } + }; + }]; +} + +/** + * @ngdoc service + * @name $q + * @requires $rootScope + * + * @description + * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). + * + * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an + * interface for interacting with an object that represents the result of an action that is + * performed asynchronously, and may or may not be finished at any given point in time. + * + * From the perspective of dealing with error handling, deferred and promise APIs are to + * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. + * + * ```js + * // for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet` + * // are available in the current lexical scope (they could have been injected or passed in). + * + * function asyncGreet(name) { + * var deferred = $q.defer(); + * + * setTimeout(function() { + * deferred.notify('About to greet ' + name + '.'); + * + * if (okToGreet(name)) { + * deferred.resolve('Hello, ' + name + '!'); + * } else { + * deferred.reject('Greeting ' + name + ' is not allowed.'); + * } + * }, 1000); + * + * return deferred.promise; + * } + * + * var promise = asyncGreet('Robin Hood'); + * promise.then(function(greeting) { + * alert('Success: ' + greeting); + * }, function(reason) { + * alert('Failed: ' + reason); + * }, function(update) { + * alert('Got notification: ' + update); + * }); + * ``` + * + * At first it might not be obvious why this extra complexity is worth the trouble. The payoff + * comes in the way of guarantees that promise and deferred APIs make, see + * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. + * + * Additionally the promise api allows for composition that is very hard to do with the + * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. + * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the + * section on serial or parallel joining of promises. + * + * + * # The Deferred API + * + * A new instance of deferred is constructed by calling `$q.defer()`. + * + * The purpose of the deferred object is to expose the associated Promise instance as well as APIs + * that can be used for signaling the successful or unsuccessful completion, as well as the status + * of the task. + * + * **Methods** + * + * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection + * constructed via `$q.reject`, the promise will be rejected instead. + * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to + * resolving it with a rejection constructed via `$q.reject`. + * - `notify(value)` - provides updates on the status of the promise's execution. This may be called + * multiple times before the promise is either resolved or rejected. + * + * **Properties** + * + * - promise – `{Promise}` – promise object associated with this deferred. + * + * + * # The Promise API + * + * A new promise instance is created when a deferred instance is created and can be retrieved by + * calling `deferred.promise`. + * + * The purpose of the promise object is to allow for interested parties to get access to the result + * of the deferred task when it completes. + * + * **Methods** + * + * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or + * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously + * as soon as the result is available. The callbacks are called with a single argument: the result + * or rejection reason. Additionally, the notify callback may be called zero or more times to + * provide a progress indication, before the promise is resolved or rejected. + * + * This method *returns a new promise* which is resolved or rejected via the return value of the + * `successCallback`, `errorCallback`. It also notifies via the return value of the + * `notifyCallback` method. The promise can not be resolved or rejected from the notifyCallback + * method. + * + * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` + * + * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise, + * but to do so without modifying the final value. This is useful to release resources or do some + * clean-up that needs to be done whether the promise was rejected or resolved. See the [full + * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for + * more information. + * + * Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as + * property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to + * make your code IE8 and Android 2.x compatible. + * + * # Chaining promises + * + * Because calling the `then` method of a promise returns a new derived promise, it is easily + * possible to create a chain of promises: + * + * ```js + * promiseB = promiseA.then(function(result) { + * return result + 1; + * }); + * + * // promiseB will be resolved immediately after promiseA is resolved and its value + * // will be the result of promiseA incremented by 1 + * ``` + * + * It is possible to create chains of any length and since a promise can be resolved with another + * promise (which will defer its resolution further), it is possible to pause/defer resolution of + * the promises at any point in the chain. This makes it possible to implement powerful APIs like + * $http's response interceptors. + * + * + * # Differences between Kris Kowal's Q and $q + * + * There are two main differences: + * + * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation + * mechanism in angular, which means faster propagation of resolution or rejection into your + * models and avoiding unnecessary browser repaints, which would result in flickering UI. + * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains + * all the important functionality needed for common async tasks. + * + * # Testing + * + * ```js + * it('should simulate promise', inject(function($q, $rootScope) { + * var deferred = $q.defer(); + * var promise = deferred.promise; + * var resolvedValue; + * + * promise.then(function(value) { resolvedValue = value; }); + * expect(resolvedValue).toBeUndefined(); + * + * // Simulate resolving of promise + * deferred.resolve(123); + * // Note that the 'then' function does not get called synchronously. + * // This is because we want the promise API to always be async, whether or not + * // it got called synchronously or asynchronously. + * expect(resolvedValue).toBeUndefined(); + * + * // Propagate promise resolution to 'then' functions using $apply(). + * $rootScope.$apply(); + * expect(resolvedValue).toEqual(123); + * })); + * ``` + */ +function $QProvider() { + + this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { + return qFactory(function(callback) { + $rootScope.$evalAsync(callback); + }, $exceptionHandler); + }]; +} + + +/** + * Constructs a promise manager. + * + * @param {function(Function)} nextTick Function for executing functions in the next turn. + * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for + * debugging purposes. + * @returns {object} Promise manager. + */ +function qFactory(nextTick, exceptionHandler) { + + /** + * @ngdoc method + * @name $q#defer + * @kind function + * + * @description + * Creates a `Deferred` object which represents a task which will finish in the future. + * + * @returns {Deferred} Returns a new instance of deferred. + */ + var defer = function() { + var pending = [], + value, deferred; + + deferred = { + + resolve: function(val) { + if (pending) { + var callbacks = pending; + pending = undefined; + value = ref(val); + + if (callbacks.length) { + nextTick(function() { + var callback; + for (var i = 0, ii = callbacks.length; i < ii; i++) { + callback = callbacks[i]; + value.then(callback[0], callback[1], callback[2]); + } + }); + } + } + }, + + + reject: function(reason) { + deferred.resolve(createInternalRejectedPromise(reason)); + }, + + + notify: function(progress) { + if (pending) { + var callbacks = pending; + + if (pending.length) { + nextTick(function() { + var callback; + for (var i = 0, ii = callbacks.length; i < ii; i++) { + callback = callbacks[i]; + callback[2](progress); + } + }); + } + } + }, + + + promise: { + then: function(callback, errback, progressback) { + var result = defer(); + + var wrappedCallback = function(value) { + try { + result.resolve((isFunction(callback) ? callback : defaultCallback)(value)); + } catch(e) { + result.reject(e); + exceptionHandler(e); + } + }; + + var wrappedErrback = function(reason) { + try { + result.resolve((isFunction(errback) ? errback : defaultErrback)(reason)); + } catch(e) { + result.reject(e); + exceptionHandler(e); + } + }; + + var wrappedProgressback = function(progress) { + try { + result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress)); + } catch(e) { + exceptionHandler(e); + } + }; + + if (pending) { + pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]); + } else { + value.then(wrappedCallback, wrappedErrback, wrappedProgressback); + } + + return result.promise; + }, + + "catch": function(callback) { + return this.then(null, callback); + }, + + "finally": function(callback) { + + function makePromise(value, resolved) { + var result = defer(); + if (resolved) { + result.resolve(value); + } else { + result.reject(value); + } + return result.promise; + } + + function handleCallback(value, isResolved) { + var callbackOutput = null; + try { + callbackOutput = (callback ||defaultCallback)(); + } catch(e) { + return makePromise(e, false); + } + if (isPromiseLike(callbackOutput)) { + return callbackOutput.then(function() { + return makePromise(value, isResolved); + }, function(error) { + return makePromise(error, false); + }); + } else { + return makePromise(value, isResolved); + } + } + + return this.then(function(value) { + return handleCallback(value, true); + }, function(error) { + return handleCallback(error, false); + }); + } + } + }; + + return deferred; + }; + + + var ref = function(value) { + if (isPromiseLike(value)) return value; + return { + then: function(callback) { + var result = defer(); + nextTick(function() { + result.resolve(callback(value)); + }); + return result.promise; + } + }; + }; + + + /** + * @ngdoc method + * @name $q#reject + * @kind function + * + * @description + * Creates a promise that is resolved as rejected with the specified `reason`. This api should be + * used to forward rejection in a chain of promises. If you are dealing with the last promise in + * a promise chain, you don't need to worry about it. + * + * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of + * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via + * a promise error callback and you want to forward the error to the promise derived from the + * current promise, you have to "rethrow" the error by returning a rejection constructed via + * `reject`. + * + * ```js + * promiseB = promiseA.then(function(result) { + * // success: do something and resolve promiseB + * // with the old or a new result + * return result; + * }, function(reason) { + * // error: handle the error if possible and + * // resolve promiseB with newPromiseOrValue, + * // otherwise forward the rejection to promiseB + * if (canHandle(reason)) { + * // handle the error and recover + * return newPromiseOrValue; + * } + * return $q.reject(reason); + * }); + * ``` + * + * @param {*} reason Constant, message, exception or an object representing the rejection reason. + * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. + */ + var reject = function(reason) { + var result = defer(); + result.reject(reason); + return result.promise; + }; + + var createInternalRejectedPromise = function(reason) { + return { + then: function(callback, errback) { + var result = defer(); + nextTick(function() { + try { + result.resolve((isFunction(errback) ? errback : defaultErrback)(reason)); + } catch(e) { + result.reject(e); + exceptionHandler(e); + } + }); + return result.promise; + } + }; + }; + + + /** + * @ngdoc method + * @name $q#when + * @kind function + * + * @description + * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. + * This is useful when you are dealing with an object that might or might not be a promise, or if + * the promise comes from a source that can't be trusted. + * + * @param {*} value Value or a promise + * @returns {Promise} Returns a promise of the passed value or promise + */ + var when = function(value, callback, errback, progressback) { + var result = defer(), + done; + + var wrappedCallback = function(value) { + try { + return (isFunction(callback) ? callback : defaultCallback)(value); + } catch (e) { + exceptionHandler(e); + return reject(e); + } + }; + + var wrappedErrback = function(reason) { + try { + return (isFunction(errback) ? errback : defaultErrback)(reason); + } catch (e) { + exceptionHandler(e); + return reject(e); + } + }; + + var wrappedProgressback = function(progress) { + try { + return (isFunction(progressback) ? progressback : defaultCallback)(progress); + } catch (e) { + exceptionHandler(e); + } + }; + + nextTick(function() { + ref(value).then(function(value) { + if (done) return; + done = true; + result.resolve(ref(value).then(wrappedCallback, wrappedErrback, wrappedProgressback)); + }, function(reason) { + if (done) return; + done = true; + result.resolve(wrappedErrback(reason)); + }, function(progress) { + if (done) return; + result.notify(wrappedProgressback(progress)); + }); + }); + + return result.promise; + }; + + + function defaultCallback(value) { + return value; + } + + + function defaultErrback(reason) { + return reject(reason); + } + + + /** + * @ngdoc method + * @name $q#all + * @kind function + * + * @description + * Combines multiple promises into a single promise that is resolved when all of the input + * promises are resolved. + * + * @param {Array.|Object.} promises An array or hash of promises. + * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, + * each value corresponding to the promise at the same index/key in the `promises` array/hash. + * If any of the promises is resolved with a rejection, this resulting promise will be rejected + * with the same rejection value. + */ + function all(promises) { + var deferred = defer(), + counter = 0, + results = isArray(promises) ? [] : {}; + + forEach(promises, function(promise, key) { + counter++; + ref(promise).then(function(value) { + if (results.hasOwnProperty(key)) return; + results[key] = value; + if (!(--counter)) deferred.resolve(results); + }, function(reason) { + if (results.hasOwnProperty(key)) return; + deferred.reject(reason); + }); + }); + + if (counter === 0) { + deferred.resolve(results); + } + + return deferred.promise; + } + + return { + defer: defer, + reject: reject, + when: when, + all: all + }; +} + +function $$RAFProvider(){ //rAF + this.$get = ['$window', '$timeout', function($window, $timeout) { + var requestAnimationFrame = $window.requestAnimationFrame || + $window.webkitRequestAnimationFrame || + $window.mozRequestAnimationFrame; + + var cancelAnimationFrame = $window.cancelAnimationFrame || + $window.webkitCancelAnimationFrame || + $window.mozCancelAnimationFrame || + $window.webkitCancelRequestAnimationFrame; + + var rafSupported = !!requestAnimationFrame; + var raf = rafSupported + ? function(fn) { + var id = requestAnimationFrame(fn); + return function() { + cancelAnimationFrame(id); + }; + } + : function(fn) { + var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666 + return function() { + $timeout.cancel(timer); + }; + }; + + raf.supported = rafSupported; + + return raf; + }]; +} + +/** + * DESIGN NOTES + * + * The design decisions behind the scope are heavily favored for speed and memory consumption. + * + * The typical use of scope is to watch the expressions, which most of the time return the same + * value as last time so we optimize the operation. + * + * Closures construction is expensive in terms of speed as well as memory: + * - No closures, instead use prototypical inheritance for API + * - Internal state needs to be stored on scope directly, which means that private state is + * exposed as $$____ properties + * + * Loop operations are optimized by using while(count--) { ... } + * - this means that in order to keep the same order of execution as addition we have to add + * items to the array at the beginning (unshift) instead of at the end (push) + * + * Child scopes are created and removed often + * - Using an array would be slow since inserts in middle are expensive so we use linked list + * + * There are few watches then a lot of observers. This is why you don't want the observer to be + * implemented in the same way as watch. Watch requires return of initialization function which + * are expensive to construct. + */ + + +/** + * @ngdoc provider + * @name $rootScopeProvider + * @description + * + * Provider for the $rootScope service. + */ + +/** + * @ngdoc method + * @name $rootScopeProvider#digestTtl + * @description + * + * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and + * assuming that the model is unstable. + * + * The current default is 10 iterations. + * + * In complex applications it's possible that the dependencies between `$watch`s will result in + * several digest iterations. However if an application needs more than the default 10 digest + * iterations for its model to stabilize then you should investigate what is causing the model to + * continuously change during the digest. + * + * Increasing the TTL could have performance implications, so you should not change it without + * proper justification. + * + * @param {number} limit The number of digest iterations. + */ + + +/** + * @ngdoc service + * @name $rootScope + * @description + * + * Every application has a single root {@link ng.$rootScope.Scope scope}. + * All other scopes are descendant scopes of the root scope. Scopes provide separation + * between the model and the view, via a mechanism for watching the model for changes. + * They also provide an event emission/broadcast and subscription facility. See the + * {@link guide/scope developer guide on scopes}. + */ +function $RootScopeProvider(){ + var TTL = 10; + var $rootScopeMinErr = minErr('$rootScope'); + var lastDirtyWatch = null; + + this.digestTtl = function(value) { + if (arguments.length) { + TTL = value; + } + return TTL; + }; + + this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser', + function( $injector, $exceptionHandler, $parse, $browser) { + + /** + * @ngdoc type + * @name $rootScope.Scope + * + * @description + * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the + * {@link auto.$injector $injector}. Child scopes are created using the + * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when + * compiled HTML template is executed.) + * + * Here is a simple scope snippet to show how you can interact with the scope. + * ```html + * + * ``` + * + * # Inheritance + * A scope can inherit from a parent scope, as in this example: + * ```js + var parent = $rootScope; + var child = parent.$new(); + + parent.salutation = "Hello"; + child.name = "World"; + expect(child.salutation).toEqual('Hello'); + + child.salutation = "Welcome"; + expect(child.salutation).toEqual('Welcome'); + expect(parent.salutation).toEqual('Hello'); + * ``` + * + * + * @param {Object.=} providers Map of service factory which need to be + * provided for the current scope. Defaults to {@link ng}. + * @param {Object.=} instanceCache Provides pre-instantiated services which should + * append/override services provided by `providers`. This is handy + * when unit-testing and having the need to override a default + * service. + * @returns {Object} Newly created scope. + * + */ + function Scope() { + this.$id = nextUid(); + this.$$phase = this.$parent = this.$$watchers = + this.$$nextSibling = this.$$prevSibling = + this.$$childHead = this.$$childTail = null; + this['this'] = this.$root = this; + this.$$destroyed = false; + this.$$asyncQueue = []; + this.$$postDigestQueue = []; + this.$$listeners = {}; + this.$$listenerCount = {}; + this.$$isolateBindings = {}; + } + + /** + * @ngdoc property + * @name $rootScope.Scope#$id + * + * @description + * Unique scope ID (monotonically increasing) useful for debugging. + */ + + /** + * @ngdoc property + * @name $rootScope.Scope#$parent + * + * @description + * Reference to the parent scope. + */ + + /** + * @ngdoc property + * @name $rootScope.Scope#$root + * + * @description + * Reference to the root scope. + */ + + Scope.prototype = { + constructor: Scope, + /** + * @ngdoc method + * @name $rootScope.Scope#$new + * @kind function + * + * @description + * Creates a new child {@link ng.$rootScope.Scope scope}. + * + * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event. + * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. + * + * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is + * desired for the scope and its child scopes to be permanently detached from the parent and + * thus stop participating in model change detection and listener notification by invoking. + * + * @param {boolean} isolate If true, then the scope does not prototypically inherit from the + * parent scope. The scope is isolated, as it can not see parent scope properties. + * When creating widgets, it is useful for the widget to not accidentally read parent + * state. + * + * @returns {Object} The newly created child scope. + * + */ + $new: function(isolate) { + var ChildScope, + child; + + if (isolate) { + child = new Scope(); + child.$root = this.$root; + // ensure that there is just one async queue per $rootScope and its children + child.$$asyncQueue = this.$$asyncQueue; + child.$$postDigestQueue = this.$$postDigestQueue; + } else { + // Only create a child scope class if somebody asks for one, + // but cache it to allow the VM to optimize lookups. + if (!this.$$childScopeClass) { + this.$$childScopeClass = function() { + this.$$watchers = this.$$nextSibling = + this.$$childHead = this.$$childTail = null; + this.$$listeners = {}; + this.$$listenerCount = {}; + this.$id = nextUid(); + this.$$childScopeClass = null; + }; + this.$$childScopeClass.prototype = this; + } + child = new this.$$childScopeClass(); + } + child['this'] = child; + child.$parent = this; + child.$$prevSibling = this.$$childTail; + if (this.$$childHead) { + this.$$childTail.$$nextSibling = child; + this.$$childTail = child; + } else { + this.$$childHead = this.$$childTail = child; + } + return child; + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$watch + * @kind function + * + * @description + * Registers a `listener` callback to be executed whenever the `watchExpression` changes. + * + * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest + * $digest()} and should return the value that will be watched. (Since + * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the + * `watchExpression` can execute multiple times per + * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) + * - The `listener` is called only when the value from the current `watchExpression` and the + * previous call to `watchExpression` are not equal (with the exception of the initial run, + * see below). Inequality is determined according to reference inequality, + * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) + * via the `!==` Javascript operator, unless `objectEquality == true` + * (see next point) + * - When `objectEquality == true`, inequality of the `watchExpression` is determined + * according to the {@link angular.equals} function. To save the value of the object for + * later comparison, the {@link angular.copy} function is used. This therefore means that + * watching complex objects will have adverse memory and performance implications. + * - The watch `listener` may change the model, which may trigger other `listener`s to fire. + * This is achieved by rerunning the watchers until no changes are detected. The rerun + * iteration limit is 10 to prevent an infinite loop deadlock. + * + * + * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, + * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` + * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a + * change is detected, be prepared for multiple calls to your listener.) + * + * After a watcher is registered with the scope, the `listener` fn is called asynchronously + * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the + * watcher. In rare cases, this is undesirable because the listener is called when the result + * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you + * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the + * listener was called due to initialization. + * + * The example below contains an illustration of using a function as your $watch listener + * + * + * # Example + * ```js + // let's assume that scope was dependency injected as the $rootScope + var scope = $rootScope; + scope.name = 'misko'; + scope.counter = 0; + + expect(scope.counter).toEqual(0); + scope.$watch('name', function(newValue, oldValue) { + scope.counter = scope.counter + 1; + }); + expect(scope.counter).toEqual(0); + + scope.$digest(); + // the listener is always called during the first $digest loop after it was registered + expect(scope.counter).toEqual(1); + + scope.$digest(); + // but now it will not be called unless the value changes + expect(scope.counter).toEqual(1); + + scope.name = 'adam'; + scope.$digest(); + expect(scope.counter).toEqual(2); + + + + // Using a listener function + var food; + scope.foodCounter = 0; + expect(scope.foodCounter).toEqual(0); + scope.$watch( + // This is the listener function + function() { return food; }, + // This is the change handler + function(newValue, oldValue) { + if ( newValue !== oldValue ) { + // Only increment the counter if the value changed + scope.foodCounter = scope.foodCounter + 1; + } + } + ); + // No digest has been run so the counter will be zero + expect(scope.foodCounter).toEqual(0); + + // Run the digest but since food has not changed count will still be zero + scope.$digest(); + expect(scope.foodCounter).toEqual(0); + + // Update food and run digest. Now the counter will increment + food = 'cheeseburger'; + scope.$digest(); + expect(scope.foodCounter).toEqual(1); + + * ``` + * + * + * + * @param {(function()|string)} watchExpression Expression that is evaluated on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers + * a call to the `listener`. + * + * - `string`: Evaluated as {@link guide/expression expression} + * - `function(scope)`: called with current `scope` as a parameter. + * @param {(function()|string)=} listener Callback called whenever the return value of + * the `watchExpression` changes. + * + * - `string`: Evaluated as {@link guide/expression expression} + * - `function(newValue, oldValue, scope)`: called with current and previous values as + * parameters. + * + * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of + * comparing for reference equality. + * @returns {function()} Returns a deregistration function for this listener. + */ + $watch: function(watchExp, listener, objectEquality) { + var scope = this, + get = compileToFn(watchExp, 'watch'), + array = scope.$$watchers, + watcher = { + fn: listener, + last: initWatchVal, + get: get, + exp: watchExp, + eq: !!objectEquality + }; + + lastDirtyWatch = null; + + // in the case user pass string, we need to compile it, do we really need this ? + if (!isFunction(listener)) { + var listenFn = compileToFn(listener || noop, 'listener'); + watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);}; + } + + if (typeof watchExp == 'string' && get.constant) { + var originalFn = watcher.fn; + watcher.fn = function(newVal, oldVal, scope) { + originalFn.call(this, newVal, oldVal, scope); + arrayRemove(array, watcher); + }; + } + + if (!array) { + array = scope.$$watchers = []; + } + // we use unshift since we use a while loop in $digest for speed. + // the while loop reads in reverse order. + array.unshift(watcher); + + return function deregisterWatch() { + arrayRemove(array, watcher); + lastDirtyWatch = null; + }; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$watchCollection + * @kind function + * + * @description + * Shallow watches the properties of an object and fires whenever any of the properties change + * (for arrays, this implies watching the array items; for object maps, this implies watching + * the properties). If a change is detected, the `listener` callback is fired. + * + * - The `obj` collection is observed via standard $watch operation and is examined on every + * call to $digest() to see if any items have been added, removed, or moved. + * - The `listener` is called whenever anything within the `obj` has changed. Examples include + * adding, removing, and moving items belonging to an object or array. + * + * + * # Example + * ```js + $scope.names = ['igor', 'matias', 'misko', 'james']; + $scope.dataCount = 4; + + $scope.$watchCollection('names', function(newNames, oldNames) { + $scope.dataCount = newNames.length; + }); + + expect($scope.dataCount).toEqual(4); + $scope.$digest(); + + //still at 4 ... no changes + expect($scope.dataCount).toEqual(4); + + $scope.names.pop(); + $scope.$digest(); + + //now there's been a change + expect($scope.dataCount).toEqual(3); + * ``` + * + * + * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The + * expression value should evaluate to an object or an array which is observed on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the + * collection will trigger a call to the `listener`. + * + * @param {function(newCollection, oldCollection, scope)} listener a callback function called + * when a change is detected. + * - The `newCollection` object is the newly modified data obtained from the `obj` expression + * - The `oldCollection` object is a copy of the former collection data. + * Due to performance considerations, the`oldCollection` value is computed only if the + * `listener` function declares two or more arguments. + * - The `scope` argument refers to the current scope. + * + * @returns {function()} Returns a de-registration function for this listener. When the + * de-registration function is executed, the internal watch operation is terminated. + */ + $watchCollection: function(obj, listener) { + var self = this; + // the current value, updated on each dirty-check run + var newValue; + // a shallow copy of the newValue from the last dirty-check run, + // updated to match newValue during dirty-check run + var oldValue; + // a shallow copy of the newValue from when the last change happened + var veryOldValue; + // only track veryOldValue if the listener is asking for it + var trackVeryOldValue = (listener.length > 1); + var changeDetected = 0; + var objGetter = $parse(obj); + var internalArray = []; + var internalObject = {}; + var initRun = true; + var oldLength = 0; + + function $watchCollectionWatch() { + newValue = objGetter(self); + var newLength, key, bothNaN; + + if (!isObject(newValue)) { // if primitive + if (oldValue !== newValue) { + oldValue = newValue; + changeDetected++; + } + } else if (isArrayLike(newValue)) { + if (oldValue !== internalArray) { + // we are transitioning from something which was not an array into array. + oldValue = internalArray; + oldLength = oldValue.length = 0; + changeDetected++; + } + + newLength = newValue.length; + + if (oldLength !== newLength) { + // if lengths do not match we need to trigger change notification + changeDetected++; + oldValue.length = oldLength = newLength; + } + // copy the items to oldValue and look for changes. + for (var i = 0; i < newLength; i++) { + bothNaN = (oldValue[i] !== oldValue[i]) && + (newValue[i] !== newValue[i]); + if (!bothNaN && (oldValue[i] !== newValue[i])) { + changeDetected++; + oldValue[i] = newValue[i]; + } + } + } else { + if (oldValue !== internalObject) { + // we are transitioning from something which was not an object into object. + oldValue = internalObject = {}; + oldLength = 0; + changeDetected++; + } + // copy the items to oldValue and look for changes. + newLength = 0; + for (key in newValue) { + if (newValue.hasOwnProperty(key)) { + newLength++; + if (oldValue.hasOwnProperty(key)) { + bothNaN = (oldValue[key] !== oldValue[key]) && + (newValue[key] !== newValue[key]); + if (!bothNaN && (oldValue[key] !== newValue[key])) { + changeDetected++; + oldValue[key] = newValue[key]; + } + } else { + oldLength++; + oldValue[key] = newValue[key]; + changeDetected++; + } + } + } + if (oldLength > newLength) { + // we used to have more keys, need to find them and destroy them. + changeDetected++; + for(key in oldValue) { + if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) { + oldLength--; + delete oldValue[key]; + } + } + } + } + return changeDetected; + } + + function $watchCollectionAction() { + if (initRun) { + initRun = false; + listener(newValue, newValue, self); + } else { + listener(newValue, veryOldValue, self); + } + + // make a copy for the next time a collection is changed + if (trackVeryOldValue) { + if (!isObject(newValue)) { + //primitive + veryOldValue = newValue; + } else if (isArrayLike(newValue)) { + veryOldValue = new Array(newValue.length); + for (var i = 0; i < newValue.length; i++) { + veryOldValue[i] = newValue[i]; + } + } else { // if object + veryOldValue = {}; + for (var key in newValue) { + if (hasOwnProperty.call(newValue, key)) { + veryOldValue[key] = newValue[key]; + } + } + } + } + } + + return this.$watch($watchCollectionWatch, $watchCollectionAction); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$digest + * @kind function + * + * @description + * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and + * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change + * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} + * until no more listeners are firing. This means that it is possible to get into an infinite + * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of + * iterations exceeds 10. + * + * Usually, you don't call `$digest()` directly in + * {@link ng.directive:ngController controllers} or in + * {@link ng.$compileProvider#directive directives}. + * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within + * a {@link ng.$compileProvider#directive directives}), which will force a `$digest()`. + * + * If you want to be notified whenever `$digest()` is called, + * you can register a `watchExpression` function with + * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. + * + * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. + * + * # Example + * ```js + var scope = ...; + scope.name = 'misko'; + scope.counter = 0; + + expect(scope.counter).toEqual(0); + scope.$watch('name', function(newValue, oldValue) { + scope.counter = scope.counter + 1; + }); + expect(scope.counter).toEqual(0); + + scope.$digest(); + // the listener is always called during the first $digest loop after it was registered + expect(scope.counter).toEqual(1); + + scope.$digest(); + // but now it will not be called unless the value changes + expect(scope.counter).toEqual(1); + + scope.name = 'adam'; + scope.$digest(); + expect(scope.counter).toEqual(2); + * ``` + * + */ + $digest: function() { + var watch, value, last, + watchers, + asyncQueue = this.$$asyncQueue, + postDigestQueue = this.$$postDigestQueue, + length, + dirty, ttl = TTL, + next, current, target = this, + watchLog = [], + logIdx, logMsg, asyncTask; + + beginPhase('$digest'); + // Check for changes to browser url that happened in sync before the call to $digest + $browser.$$checkUrlChange(); + + lastDirtyWatch = null; + + do { // "while dirty" loop + dirty = false; + current = target; + + while(asyncQueue.length) { + try { + asyncTask = asyncQueue.shift(); + asyncTask.scope.$eval(asyncTask.expression); + } catch (e) { + clearPhase(); + $exceptionHandler(e); + } + lastDirtyWatch = null; + } + + traverseScopesLoop: + do { // "traverse the scopes" loop + if ((watchers = current.$$watchers)) { + // process our watches + length = watchers.length; + while (length--) { + try { + watch = watchers[length]; + // Most common watches are on primitives, in which case we can short + // circuit it with === operator, only when === fails do we use .equals + if (watch) { + if ((value = watch.get(current)) !== (last = watch.last) && + !(watch.eq + ? equals(value, last) + : (typeof value === 'number' && typeof last === 'number' + && isNaN(value) && isNaN(last)))) { + dirty = true; + lastDirtyWatch = watch; + watch.last = watch.eq ? copy(value, null) : value; + watch.fn(value, ((last === initWatchVal) ? value : last), current); + if (ttl < 5) { + logIdx = 4 - ttl; + if (!watchLog[logIdx]) watchLog[logIdx] = []; + logMsg = (isFunction(watch.exp)) + ? 'fn: ' + (watch.exp.name || watch.exp.toString()) + : watch.exp; + logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last); + watchLog[logIdx].push(logMsg); + } + } else if (watch === lastDirtyWatch) { + // If the most recently dirty watcher is now clean, short circuit since the remaining watchers + // have already been tested. + dirty = false; + break traverseScopesLoop; + } + } + } catch (e) { + clearPhase(); + $exceptionHandler(e); + } + } + } + + // Insanity Warning: scope depth-first traversal + // yes, this code is a bit crazy, but it works and we have tests to prove it! + // this piece should be kept in sync with the traversal in $broadcast + if (!(next = (current.$$childHead || + (current !== target && current.$$nextSibling)))) { + while(current !== target && !(next = current.$$nextSibling)) { + current = current.$parent; + } + } + } while ((current = next)); + + // `break traverseScopesLoop;` takes us to here + + if((dirty || asyncQueue.length) && !(ttl--)) { + clearPhase(); + throw $rootScopeMinErr('infdig', + '{0} $digest() iterations reached. Aborting!\n' + + 'Watchers fired in the last 5 iterations: {1}', + TTL, toJson(watchLog)); + } + + } while (dirty || asyncQueue.length); + + clearPhase(); + + while(postDigestQueue.length) { + try { + postDigestQueue.shift()(); + } catch (e) { + $exceptionHandler(e); + } + } + }, + + + /** + * @ngdoc event + * @name $rootScope.Scope#$destroy + * @eventType broadcast on scope being destroyed + * + * @description + * Broadcasted when a scope and its children are being destroyed. + * + * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to + * clean up DOM bindings before an element is removed from the DOM. + */ + + /** + * @ngdoc method + * @name $rootScope.Scope#$destroy + * @kind function + * + * @description + * Removes the current scope (and all of its children) from the parent scope. Removal implies + * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer + * propagate to the current scope and its children. Removal also implies that the current + * scope is eligible for garbage collection. + * + * The `$destroy()` is usually used by directives such as + * {@link ng.directive:ngRepeat ngRepeat} for managing the + * unrolling of the loop. + * + * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope. + * Application code can register a `$destroy` event handler that will give it a chance to + * perform any necessary cleanup. + * + * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to + * clean up DOM bindings before an element is removed from the DOM. + */ + $destroy: function() { + // we can't destroy the root scope or a scope that has been already destroyed + if (this.$$destroyed) return; + var parent = this.$parent; + + this.$broadcast('$destroy'); + this.$$destroyed = true; + if (this === $rootScope) return; + + forEach(this.$$listenerCount, bind(null, decrementListenerCount, this)); + + // sever all the references to parent scopes (after this cleanup, the current scope should + // not be retained by any of our references and should be eligible for garbage collection) + if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; + if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; + if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; + if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; + + + // All of the code below is bogus code that works around V8's memory leak via optimized code + // and inline caches. + // + // see: + // - https://code.google.com/p/v8/issues/detail?id=2073#c26 + // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 + // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 + + this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = + this.$$childTail = this.$root = null; + + // don't reset these to null in case some async task tries to register a listener/watch/task + this.$$listeners = {}; + this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = []; + + // prevent NPEs since these methods have references to properties we nulled out + this.$destroy = this.$digest = this.$apply = noop; + this.$on = this.$watch = function() { return noop; }; + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$eval + * @kind function + * + * @description + * Executes the `expression` on the current scope and returns the result. Any exceptions in + * the expression are propagated (uncaught). This is useful when evaluating Angular + * expressions. + * + * # Example + * ```js + var scope = ng.$rootScope.Scope(); + scope.a = 1; + scope.b = 2; + + expect(scope.$eval('a+b')).toEqual(3); + expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); + * ``` + * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + * @param {(object)=} locals Local variables object, useful for overriding values in scope. + * @returns {*} The result of evaluating the expression. + */ + $eval: function(expr, locals) { + return $parse(expr)(this, locals); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$evalAsync + * @kind function + * + * @description + * Executes the expression on the current scope at a later point in time. + * + * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only + * that: + * + * - it will execute after the function that scheduled the evaluation (preferably before DOM + * rendering). + * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after + * `expression` execution. + * + * Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle + * will be scheduled. However, it is encouraged to always call code that changes the model + * from within an `$apply` call. That includes code evaluated via `$evalAsync`. + * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + */ + $evalAsync: function(expr) { + // if we are outside of an $digest loop and this is the first time we are scheduling async + // task also schedule async auto-flush + if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) { + $browser.defer(function() { + if ($rootScope.$$asyncQueue.length) { + $rootScope.$digest(); + } + }); + } + + this.$$asyncQueue.push({scope: this, expression: expr}); + }, + + $$postDigest : function(fn) { + this.$$postDigestQueue.push(fn); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$apply + * @kind function + * + * @description + * `$apply()` is used to execute an expression in angular from outside of the angular + * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). + * Because we are calling into the angular framework we need to perform proper scope life + * cycle of {@link ng.$exceptionHandler exception handling}, + * {@link ng.$rootScope.Scope#$digest executing watches}. + * + * ## Life cycle + * + * # Pseudo-Code of `$apply()` + * ```js + function $apply(expr) { + try { + return $eval(expr); + } catch (e) { + $exceptionHandler(e); + } finally { + $root.$digest(); + } + } + * ``` + * + * + * Scope's `$apply()` method transitions through the following stages: + * + * 1. The {@link guide/expression expression} is executed using the + * {@link ng.$rootScope.Scope#$eval $eval()} method. + * 2. Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the + * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. + * + * + * @param {(string|function())=} exp An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with current `scope` parameter. + * + * @returns {*} The result of evaluating the expression. + */ + $apply: function(expr) { + try { + beginPhase('$apply'); + return this.$eval(expr); + } catch (e) { + $exceptionHandler(e); + } finally { + clearPhase(); + try { + $rootScope.$digest(); + } catch (e) { + $exceptionHandler(e); + throw e; + } + } + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$on + * @kind function + * + * @description + * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for + * discussion of event life cycle. + * + * The event listener function format is: `function(event, args...)`. The `event` object + * passed into the listener has the following attributes: + * + * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or + * `$broadcast`-ed. + * - `currentScope` - `{Scope}`: the current scope which is handling the event. + * - `name` - `{string}`: name of the event. + * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel + * further event propagation (available only for events that were `$emit`-ed). + * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag + * to true. + * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. + * + * @param {string} name Event name to listen on. + * @param {function(event, ...args)} listener Function to call when the event is emitted. + * @returns {function()} Returns a deregistration function for this listener. + */ + $on: function(name, listener) { + var namedListeners = this.$$listeners[name]; + if (!namedListeners) { + this.$$listeners[name] = namedListeners = []; + } + namedListeners.push(listener); + + var current = this; + do { + if (!current.$$listenerCount[name]) { + current.$$listenerCount[name] = 0; + } + current.$$listenerCount[name]++; + } while ((current = current.$parent)); + + var self = this; + return function() { + namedListeners[indexOf(namedListeners, listener)] = null; + decrementListenerCount(self, 1, name); + }; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$emit + * @kind function + * + * @description + * Dispatches an event `name` upwards through the scope hierarchy notifying the + * registered {@link ng.$rootScope.Scope#$on} listeners. + * + * The event life cycle starts at the scope on which `$emit` was called. All + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get + * notified. Afterwards, the event traverses upwards toward the root scope and calls all + * registered listeners along the way. The event will stop propagating if one of the listeners + * cancels it. + * + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * onto the {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {string} name Event name to emit. + * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. + * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}). + */ + $emit: function(name, args) { + var empty = [], + namedListeners, + scope = this, + stopPropagation = false, + event = { + name: name, + targetScope: scope, + stopPropagation: function() {stopPropagation = true;}, + preventDefault: function() { + event.defaultPrevented = true; + }, + defaultPrevented: false + }, + listenerArgs = concat([event], arguments, 1), + i, length; + + do { + namedListeners = scope.$$listeners[name] || empty; + event.currentScope = scope; + for (i=0, length=namedListeners.length; i= 8 ) { + normalizedVal = urlResolve(uri).href; + if (normalizedVal !== '' && !normalizedVal.match(regex)) { + return 'unsafe:'+normalizedVal; + } + } + return uri; + }; + }; +} + +var $sceMinErr = minErr('$sce'); + +var SCE_CONTEXTS = { + HTML: 'html', + CSS: 'css', + URL: 'url', + // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a + // url. (e.g. ng-include, script src, templateUrl) + RESOURCE_URL: 'resourceUrl', + JS: 'js' +}; + +// Helper functions follow. + +// Copied from: +// http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962 +// Prereq: s is a string. +function escapeForRegexp(s) { + return s.replace(/([-()\[\]{}+?*.$\^|,:# -1) { + throw $sceMinErr('iwcard', + 'Illegal sequence *** in string matcher. String: {0}', matcher); + } + matcher = escapeForRegexp(matcher). + replace('\\*\\*', '.*'). + replace('\\*', '[^:/.?&;]*'); + return new RegExp('^' + matcher + '$'); + } else if (isRegExp(matcher)) { + // The only other type of matcher allowed is a Regexp. + // Match entire URL / disallow partial matches. + // Flags are reset (i.e. no global, ignoreCase or multiline) + return new RegExp('^' + matcher.source + '$'); + } else { + throw $sceMinErr('imatcher', + 'Matchers may only be "self", string patterns or RegExp objects'); + } +} + + +function adjustMatchers(matchers) { + var adjustedMatchers = []; + if (isDefined(matchers)) { + forEach(matchers, function(matcher) { + adjustedMatchers.push(adjustMatcher(matcher)); + }); + } + return adjustedMatchers; +} + + +/** + * @ngdoc service + * @name $sceDelegate + * @kind function + * + * @description + * + * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict + * Contextual Escaping (SCE)} services to AngularJS. + * + * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of + * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is + * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to + * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things + * work because `$sce` delegates to `$sceDelegate` for these operations. + * + * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service. + * + * The default instance of `$sceDelegate` should work out of the box with little pain. While you + * can override it completely to change the behavior of `$sce`, the common case would + * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting + * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as + * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist + * $sceDelegateProvider.resourceUrlWhitelist} and {@link + * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + */ + +/** + * @ngdoc provider + * @name $sceDelegateProvider + * @description + * + * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate + * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure + * that the URLs used for sourcing Angular templates are safe. Refer {@link + * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and + * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + * + * For the general details about this service in Angular, read the main page for {@link ng.$sce + * Strict Contextual Escaping (SCE)}. + * + * **Example**: Consider the following case. + * + * - your app is hosted at url `http://myapp.example.com/` + * - but some of your templates are hosted on other domains you control such as + * `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc. + * - and you have an open redirect at `http://myapp.example.com/clickThru?...`. + * + * Here is what a secure configuration for this scenario might look like: + * + * ``` + * angular.module('myApp', []).config(function($sceDelegateProvider) { + * $sceDelegateProvider.resourceUrlWhitelist([ + * // Allow same origin resource loads. + * 'self', + * // Allow loading from our assets domain. Notice the difference between * and **. + * 'http://srv*.assets.example.com/**' + * ]); + * + * // The blacklist overrides the whitelist so the open redirect here is blocked. + * $sceDelegateProvider.resourceUrlBlacklist([ + * 'http://myapp.example.com/clickThru**' + * ]); + * }); + * ``` + */ + +function $SceDelegateProvider() { + this.SCE_CONTEXTS = SCE_CONTEXTS; + + // Resource URLs can also be trusted by policy. + var resourceUrlWhitelist = ['self'], + resourceUrlBlacklist = []; + + /** + * @ngdoc method + * @name $sceDelegateProvider#resourceUrlWhitelist + * @kind function + * + * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored. + * + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. + * + * Note: **an empty whitelist array will block all URLs**! + * + * @return {Array} the currently set whitelist array. + * + * The **default value** when no whitelist has been explicitly set is `['self']` allowing only + * same origin resource requests. + * + * @description + * Sets/Gets the whitelist of trusted resource URLs. + */ + this.resourceUrlWhitelist = function (value) { + if (arguments.length) { + resourceUrlWhitelist = adjustMatchers(value); + } + return resourceUrlWhitelist; + }; + + /** + * @ngdoc method + * @name $sceDelegateProvider#resourceUrlBlacklist + * @kind function + * + * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored. + * + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. + * + * The typical usage for the blacklist is to **block + * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as + * these would otherwise be trusted but actually return content from the redirected domain. + * + * Finally, **the blacklist overrides the whitelist** and has the final say. + * + * @return {Array} the currently set blacklist array. + * + * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there + * is no blacklist.) + * + * @description + * Sets/Gets the blacklist of trusted resource URLs. + */ + + this.resourceUrlBlacklist = function (value) { + if (arguments.length) { + resourceUrlBlacklist = adjustMatchers(value); + } + return resourceUrlBlacklist; + }; + + this.$get = ['$injector', function($injector) { + + var htmlSanitizer = function htmlSanitizer(html) { + throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); + }; + + if ($injector.has('$sanitize')) { + htmlSanitizer = $injector.get('$sanitize'); + } + + + function matchUrl(matcher, parsedUrl) { + if (matcher === 'self') { + return urlIsSameOrigin(parsedUrl); + } else { + // definitely a regex. See adjustMatchers() + return !!matcher.exec(parsedUrl.href); + } + } + + function isResourceUrlAllowedByPolicy(url) { + var parsedUrl = urlResolve(url.toString()); + var i, n, allowed = false; + // Ensure that at least one item from the whitelist allows this url. + for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) { + if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) { + allowed = true; + break; + } + } + if (allowed) { + // Ensure that no item from the blacklist blocked this url. + for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) { + if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) { + allowed = false; + break; + } + } + } + return allowed; + } + + function generateHolderType(Base) { + var holderType = function TrustedValueHolderType(trustedValue) { + this.$$unwrapTrustedValue = function() { + return trustedValue; + }; + }; + if (Base) { + holderType.prototype = new Base(); + } + holderType.prototype.valueOf = function sceValueOf() { + return this.$$unwrapTrustedValue(); + }; + holderType.prototype.toString = function sceToString() { + return this.$$unwrapTrustedValue().toString(); + }; + return holderType; + } + + var trustedValueHolderBase = generateHolderType(), + byType = {}; + + byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]); + + /** + * @ngdoc method + * @name $sceDelegate#trustAs + * + * @description + * Returns an object that is trusted by angular for use in specified strict + * contextual escaping contexts (such as ng-bind-html, ng-include, any src + * attribute interpolation, any dom event binding attribute interpolation + * such as for onclick, etc.) that uses the provided value. + * See {@link ng.$sce $sce} for enabling strict contextual escaping. + * + * @param {string} type The kind of context in which this value is safe for use. e.g. url, + * resourceUrl, html, js and css. + * @param {*} value The value that that should be considered trusted/safe. + * @returns {*} A value that can be used to stand in for the provided `value` in places + * where Angular expects a $sce.trustAs() return value. + */ + function trustAs(type, trustedValue) { + var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + if (!Constructor) { + throw $sceMinErr('icontext', + 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', + type, trustedValue); + } + if (trustedValue === null || trustedValue === undefined || trustedValue === '') { + return trustedValue; + } + // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting + // mutable objects, we ensure here that the value passed in is actually a string. + if (typeof trustedValue !== 'string') { + throw $sceMinErr('itype', + 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', + type); + } + return new Constructor(trustedValue); + } + + /** + * @ngdoc method + * @name $sceDelegate#valueOf + * + * @description + * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. + * + * If the passed parameter is not a value that had been returned by {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. + * + * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} + * call or anything else. + * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns + * `value` unchanged. + */ + function valueOf(maybeTrusted) { + if (maybeTrusted instanceof trustedValueHolderBase) { + return maybeTrusted.$$unwrapTrustedValue(); + } else { + return maybeTrusted; + } + } + + /** + * @ngdoc method + * @name $sceDelegate#getTrusted + * + * @description + * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and + * returns the originally supplied value if the queried context type is a supertype of the + * created type. If this condition isn't satisfied, throws an exception. + * + * @param {string} type The kind of context in which this value is to be used. + * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} call. + * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. + */ + function getTrusted(type, maybeTrusted) { + if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') { + return maybeTrusted; + } + var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + if (constructor && maybeTrusted instanceof constructor) { + return maybeTrusted.$$unwrapTrustedValue(); + } + // If we get here, then we may only take one of two actions. + // 1. sanitize the value for the requested type, or + // 2. throw an exception. + if (type === SCE_CONTEXTS.RESOURCE_URL) { + if (isResourceUrlAllowedByPolicy(maybeTrusted)) { + return maybeTrusted; + } else { + throw $sceMinErr('insecurl', + 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', + maybeTrusted.toString()); + } + } else if (type === SCE_CONTEXTS.HTML) { + return htmlSanitizer(maybeTrusted); + } + throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); + } + + return { trustAs: trustAs, + getTrusted: getTrusted, + valueOf: valueOf }; + }]; +} + + +/** + * @ngdoc provider + * @name $sceProvider + * @description + * + * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service. + * - enable/disable Strict Contextual Escaping (SCE) in a module + * - override the default implementation with a custom delegate + * + * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. + */ + +/* jshint maxlen: false*/ + +/** + * @ngdoc service + * @name $sce + * @kind function + * + * @description + * + * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS. + * + * # Strict Contextual Escaping + * + * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain + * contexts to result in a value that is marked as safe to use for that context. One example of + * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer + * to these contexts as privileged or SCE contexts. + * + * As of version 1.2, Angular ships with SCE enabled by default. + * + * Note: When enabled (the default), IE8 in quirks mode is not supported. In this mode, IE8 allows + * one to execute arbitrary javascript by the use of the expression() syntax. Refer + * to learn more about them. + * You can ensure your document is in standards mode and not quirks mode by adding `` + * to the top of your HTML document. + * + * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for + * security vulnerabilities such as XSS, clickjacking, etc. a lot easier. + * + * Here's an example of a binding in a privileged context: + * + * ``` + * + *
+ * ``` + * + * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE + * disabled, this application allows the user to render arbitrary HTML into the DIV. + * In a more realistic example, one may be rendering user comments, blog articles, etc. via + * bindings. (HTML is just one example of a context where rendering user controlled input creates + * security vulnerabilities.) + * + * For the case of HTML, you might use a library, either on the client side, or on the server side, + * to sanitize unsafe HTML before binding to the value and rendering it in the document. + * + * How would you ensure that every place that used these types of bindings was bound to a value that + * was sanitized by your library (or returned as safe for rendering by your server?) How can you + * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some + * properties/fields and forgot to update the binding to the sanitized value? + * + * To be secure by default, you want to ensure that any such bindings are disallowed unless you can + * determine that something explicitly says it's safe to use a value for binding in that + * context. You can then audit your code (a simple grep would do) to ensure that this is only done + * for those values that you can easily tell are safe - because they were received from your server, + * sanitized by your library, etc. You can organize your codebase to help with this - perhaps + * allowing only the files in a specific directory to do this. Ensuring that the internal API + * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. + * + * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} + * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to + * obtain values that will be accepted by SCE / privileged contexts. + * + * + * ## How does it work? + * + * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted + * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link + * ng.$sce#parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the + * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. + * + * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link + * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly + * simplified): + * + * ``` + * var ngBindHtmlDirective = ['$sce', function($sce) { + * return function(scope, element, attr) { + * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) { + * element.html(value || ''); + * }); + * }; + * }]; + * ``` + * + * ## Impact on loading templates + * + * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as + * `templateUrl`'s specified by {@link guide/directive directives}. + * + * By default, Angular only loads templates from the same domain and protocol as the application + * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl + * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or + * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist + * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. + * + * *Please note*: + * The browser's + * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) + * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) + * policy apply in addition to this and may further restrict whether the template is successfully + * loaded. This means that without the right CORS policy, loading templates from a different domain + * won't work on all browsers. Also, loading templates from `file://` URL does not work on some + * browsers. + * + * ## This feels like too much overhead for the developer? + * + * It's important to remember that SCE only applies to interpolation expressions. + * + * If your expressions are constant literals, they're automatically trusted and you don't need to + * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g. + * `
`) just works. + * + * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them + * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. + * + * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load + * templates in `ng-include` from your application's domain without having to even know about SCE. + * It blocks loading templates from other domains or loading templates over http from an https + * served document. You can change these by setting your own custom {@link + * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link + * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. + * + * This significantly reduces the overhead. It is far easier to pay the small overhead and have an + * application that's secure and can be audited to verify that with much more ease than bolting + * security onto an application later. + * + * + * ## What trusted context types are supported? + * + * | Context | Notes | + * |---------------------|----------------| + * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. | + * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | + * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`
Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | + * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | + * + * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist}
+ * + * Each element in these arrays must be one of the following: + * + * - **'self'** + * - The special **string**, `'self'`, can be used to match against all URLs of the **same + * domain** as the application document using the **same protocol**. + * - **String** (except the special value `'self'`) + * - The string is matched against the full *normalized / absolute URL* of the resource + * being tested (substring matches are not good enough.) + * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters + * match themselves. + * - `*`: matches zero or more occurrences of any character other than one of the following 6 + * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use + * in a whitelist. + * - `**`: matches zero or more occurrences of *any* character. As such, it's not + * not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g. + * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might + * not have been the intention.) Its usage at the very end of the path is ok. (e.g. + * http://foo.example.com/templates/**). + * - **RegExp** (*see caveat below*) + * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax + * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to + * accidentally introduce a bug when one updates a complex expression (imho, all regexes should + * have good test coverage.). For instance, the use of `.` in the regex is correct only in a + * small number of cases. A `.` character in the regex used when matching the scheme or a + * subdomain could be matched against a `:` or literal `.` that was likely not intended. It + * is highly recommended to use the string patterns and only fall back to regular expressions + * if they as a last resort. + * - The regular expression must be an instance of RegExp (i.e. not a string.) It is + * matched against the **entire** *normalized / absolute URL* of the resource being tested + * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags + * present on the RegExp (such as multiline, global, ignoreCase) are ignored. + * - If you are generating your JavaScript from some other templating engine (not + * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)), + * remember to escape your regular expression (and be aware that you might need more than + * one level of escaping depending on your templating engine and the way you interpolated + * the value.) Do make use of your platform's escaping mechanism as it might be good + * enough before coding your own. e.g. Ruby has + * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape) + * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). + * Javascript lacks a similar built in function for escaping. Take a look at Google + * Closure library's [goog.string.regExpEscape(s)]( + * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). + * + * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. + * + * ## Show me an example using SCE. + * + * + * + *
+ *

+ * User comments
+ * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when + * $sanitize is available. If $sanitize isn't available, this results in an error instead of an + * exploit. + *
+ *
+ * {{userComment.name}}: + * + *
+ *
+ *
+ *
+ *
+ * + * + * var mySceApp = angular.module('mySceApp', ['ngSanitize']); + * + * mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) { + * var self = this; + * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) { + * self.userComments = userComments; + * }); + * self.explicitlyTrustedHtml = $sce.trustAsHtml( + * 'Hover over this text.'); + * }); + * + * + * + * [ + * { "name": "Alice", + * "htmlComment": + * "Is anyone reading this?" + * }, + * { "name": "Bob", + * "htmlComment": "Yes! Am I the only other one?" + * } + * ] + * + * + * + * describe('SCE doc demo', function() { + * it('should sanitize untrusted values', function() { + * expect(element.all(by.css('.htmlComment')).first().getInnerHtml()) + * .toBe('Is anyone reading this?'); + * }); + * + * it('should NOT sanitize explicitly trusted values', function() { + * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe( + * 'Hover over this text.'); + * }); + * }); + * + *
+ * + * + * + * ## Can I disable SCE completely? + * + * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits + * for little coding overhead. It will be much harder to take an SCE disabled application and + * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE + * for cases where you have a lot of existing code that was written before SCE was introduced and + * you're migrating them a module at a time. + * + * That said, here's how you can completely disable SCE: + * + * ``` + * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { + * // Completely disable SCE. For demonstration purposes only! + * // Do not use in new projects. + * $sceProvider.enabled(false); + * }); + * ``` + * + */ +/* jshint maxlen: 100 */ + +function $SceProvider() { + var enabled = true; + + /** + * @ngdoc method + * @name $sceProvider#enabled + * @kind function + * + * @param {boolean=} value If provided, then enables/disables SCE. + * @return {boolean} true if SCE is enabled, false otherwise. + * + * @description + * Enables/disables SCE and returns the current value. + */ + this.enabled = function (value) { + if (arguments.length) { + enabled = !!value; + } + return enabled; + }; + + + /* Design notes on the default implementation for SCE. + * + * The API contract for the SCE delegate + * ------------------------------------- + * The SCE delegate object must provide the following 3 methods: + * + * - trustAs(contextEnum, value) + * This method is used to tell the SCE service that the provided value is OK to use in the + * contexts specified by contextEnum. It must return an object that will be accepted by + * getTrusted() for a compatible contextEnum and return this value. + * + * - valueOf(value) + * For values that were not produced by trustAs(), return them as is. For values that were + * produced by trustAs(), return the corresponding input value to trustAs. Basically, if + * trustAs is wrapping the given values into some type, this operation unwraps it when given + * such a value. + * + * - getTrusted(contextEnum, value) + * This function should return the a value that is safe to use in the context specified by + * contextEnum or throw and exception otherwise. + * + * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be + * opaque or wrapped in some holder object. That happens to be an implementation detail. For + * instance, an implementation could maintain a registry of all trusted objects by context. In + * such a case, trustAs() would return the same object that was passed in. getTrusted() would + * return the same object passed in if it was found in the registry under a compatible context or + * throw an exception otherwise. An implementation might only wrap values some of the time based + * on some criteria. getTrusted() might return a value and not throw an exception for special + * constants or objects even if not wrapped. All such implementations fulfill this contract. + * + * + * A note on the inheritance model for SCE contexts + * ------------------------------------------------ + * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This + * is purely an implementation details. + * + * The contract is simply this: + * + * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) + * will also succeed. + * + * Inheritance happens to capture this in a natural way. In some future, we + * may not use inheritance anymore. That is OK because no code outside of + * sce.js and sceSpecs.js would need to be aware of this detail. + */ + + this.$get = ['$parse', '$sniffer', '$sceDelegate', function( + $parse, $sniffer, $sceDelegate) { + // Prereq: Ensure that we're not running in IE8 quirks mode. In that mode, IE allows + // the "expression(javascript expression)" syntax which is insecure. + if (enabled && $sniffer.msie && $sniffer.msieDocumentMode < 8) { + throw $sceMinErr('iequirks', + 'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' + + 'mode. You can fix this by adding the text to the top of your HTML ' + + 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); + } + + var sce = shallowCopy(SCE_CONTEXTS); + + /** + * @ngdoc method + * @name $sce#isEnabled + * @kind function + * + * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you + * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. + * + * @description + * Returns a boolean indicating if SCE is enabled. + */ + sce.isEnabled = function () { + return enabled; + }; + sce.trustAs = $sceDelegate.trustAs; + sce.getTrusted = $sceDelegate.getTrusted; + sce.valueOf = $sceDelegate.valueOf; + + if (!enabled) { + sce.trustAs = sce.getTrusted = function(type, value) { return value; }; + sce.valueOf = identity; + } + + /** + * @ngdoc method + * @name $sce#parseAs + * + * @description + * Converts Angular {@link guide/expression expression} into a function. This is like {@link + * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it + * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, + * *result*)} + * + * @param {string} type The kind of SCE context in which this result will be used. + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + sce.parseAs = function sceParseAs(type, expr) { + var parsed = $parse(expr); + if (parsed.literal && parsed.constant) { + return parsed; + } else { + return function sceParseAsTrusted(self, locals) { + return sce.getTrusted(type, parsed(self, locals)); + }; + } + }; + + /** + * @ngdoc method + * @name $sce#trustAs + * + * @description + * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, + * returns an object that is trusted by angular for use in specified strict contextual + * escaping contexts (such as ng-bind-html, ng-include, any src attribute + * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) + * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual + * escaping. + * + * @param {string} type The kind of context in which this value is safe for use. e.g. url, + * resource_url, html, js and css. + * @param {*} value The value that that should be considered trusted/safe. + * @returns {*} A value that can be used to stand in for the provided `value` in places + * where Angular expects a $sce.trustAs() return value. + */ + + /** + * @ngdoc method + * @name $sce#trustAsHtml + * + * @description + * Shorthand method. `$sce.trustAsHtml(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml + * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the + * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#trustAsUrl + * + * @description + * Shorthand method. `$sce.trustAsUrl(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl + * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the + * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#trustAsResourceUrl + * + * @description + * Shorthand method. `$sce.trustAsResourceUrl(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl + * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the return + * value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#trustAsJs + * + * @description + * Shorthand method. `$sce.trustAsJs(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs + * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the + * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#getTrusted + * + * @description + * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, + * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the + * originally supplied value if the queried context type is a supertype of the created type. + * If this condition isn't satisfied, throws an exception. + * + * @param {string} type The kind of context in which this value is to be used. + * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} + * call. + * @returns {*} The value the was originally provided to + * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context. + * Otherwise, throws an exception. + */ + + /** + * @ngdoc method + * @name $sce#getTrustedHtml + * + * @description + * Shorthand method. `$sce.getTrustedHtml(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedCss + * + * @description + * Shorthand method. `$sce.getTrustedCss(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedUrl + * + * @description + * Shorthand method. `$sce.getTrustedUrl(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedResourceUrl + * + * @description + * Shorthand method. `$sce.getTrustedResourceUrl(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} + * + * @param {*} value The value to pass to `$sceDelegate.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedJs + * + * @description + * Shorthand method. `$sce.getTrustedJs(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` + */ + + /** + * @ngdoc method + * @name $sce#parseAsHtml + * + * @description + * Shorthand method. `$sce.parseAsHtml(expression string)` → + * {@link ng.$sce#parse `$sce.parseAs($sce.HTML, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsCss + * + * @description + * Shorthand method. `$sce.parseAsCss(value)` → + * {@link ng.$sce#parse `$sce.parseAs($sce.CSS, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsUrl + * + * @description + * Shorthand method. `$sce.parseAsUrl(value)` → + * {@link ng.$sce#parse `$sce.parseAs($sce.URL, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsResourceUrl + * + * @description + * Shorthand method. `$sce.parseAsResourceUrl(value)` → + * {@link ng.$sce#parse `$sce.parseAs($sce.RESOURCE_URL, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsJs + * + * @description + * Shorthand method. `$sce.parseAsJs(value)` → + * {@link ng.$sce#parse `$sce.parseAs($sce.JS, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + // Shorthand delegations. + var parse = sce.parseAs, + getTrusted = sce.getTrusted, + trustAs = sce.trustAs; + + forEach(SCE_CONTEXTS, function (enumValue, name) { + var lName = lowercase(name); + sce[camelCase("parse_as_" + lName)] = function (expr) { + return parse(enumValue, expr); + }; + sce[camelCase("get_trusted_" + lName)] = function (value) { + return getTrusted(enumValue, value); + }; + sce[camelCase("trust_as_" + lName)] = function (value) { + return trustAs(enumValue, value); + }; + }); + + return sce; + }]; +} + +/** + * !!! This is an undocumented "private" service !!! + * + * @name $sniffer + * @requires $window + * @requires $document + * + * @property {boolean} history Does the browser support html5 history api ? + * @property {boolean} hashchange Does the browser support hashchange event ? + * @property {boolean} transitions Does the browser support CSS transition events ? + * @property {boolean} animations Does the browser support CSS animation events ? + * + * @description + * This is very simple implementation of testing browser's features. + */ +function $SnifferProvider() { + this.$get = ['$window', '$document', function($window, $document) { + var eventSupport = {}, + android = + int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), + boxee = /Boxee/i.test(($window.navigator || {}).userAgent), + document = $document[0] || {}, + documentMode = document.documentMode, + vendorPrefix, + vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/, + bodyStyle = document.body && document.body.style, + transitions = false, + animations = false, + match; + + if (bodyStyle) { + for(var prop in bodyStyle) { + if(match = vendorRegex.exec(prop)) { + vendorPrefix = match[0]; + vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1); + break; + } + } + + if(!vendorPrefix) { + vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit'; + } + + transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle)); + animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle)); + + if (android && (!transitions||!animations)) { + transitions = isString(document.body.style.webkitTransition); + animations = isString(document.body.style.webkitAnimation); + } + } + + + return { + // Android has history.pushState, but it does not update location correctly + // so let's not use the history API at all. + // http://code.google.com/p/android/issues/detail?id=17471 + // https://github.com/angular/angular.js/issues/904 + + // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has + // so let's not use the history API also + // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined + // jshint -W018 + history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee), + // jshint +W018 + hashchange: 'onhashchange' in $window && + // IE8 compatible mode lies + (!documentMode || documentMode > 7), + hasEvent: function(event) { + // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have + // it. In particular the event is not fired when backspace or delete key are pressed or + // when cut operation is performed. + if (event == 'input' && msie == 9) return false; + + if (isUndefined(eventSupport[event])) { + var divElm = document.createElement('div'); + eventSupport[event] = 'on' + event in divElm; + } + + return eventSupport[event]; + }, + csp: csp(), + vendorPrefix: vendorPrefix, + transitions : transitions, + animations : animations, + android: android, + msie : msie, + msieDocumentMode: documentMode + }; + }]; +} + +function $TimeoutProvider() { + this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler', + function($rootScope, $browser, $q, $exceptionHandler) { + var deferreds = {}; + + + /** + * @ngdoc service + * @name $timeout + * + * @description + * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch + * block and delegates any exceptions to + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * The return value of registering a timeout function is a promise, which will be resolved when + * the timeout is reached and the timeout function is executed. + * + * To cancel a timeout request, call `$timeout.cancel(promise)`. + * + * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to + * synchronously flush the queue of deferred functions. + * + * @param {function()} fn A function, whose execution should be delayed. + * @param {number=} [delay=0] Delay in milliseconds. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this + * promise will be resolved with is the return value of the `fn` function. + * + */ + function timeout(fn, delay, invokeApply) { + var deferred = $q.defer(), + promise = deferred.promise, + skipApply = (isDefined(invokeApply) && !invokeApply), + timeoutId; + + timeoutId = $browser.defer(function() { + try { + deferred.resolve(fn()); + } catch(e) { + deferred.reject(e); + $exceptionHandler(e); + } + finally { + delete deferreds[promise.$$timeoutId]; + } + + if (!skipApply) $rootScope.$apply(); + }, delay); + + promise.$$timeoutId = timeoutId; + deferreds[timeoutId] = deferred; + + return promise; + } + + + /** + * @ngdoc method + * @name $timeout#cancel + * + * @description + * Cancels a task associated with the `promise`. As a result of this, the promise will be + * resolved with a rejection. + * + * @param {Promise=} promise Promise returned by the `$timeout` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + timeout.cancel = function(promise) { + if (promise && promise.$$timeoutId in deferreds) { + deferreds[promise.$$timeoutId].reject('canceled'); + delete deferreds[promise.$$timeoutId]; + return $browser.defer.cancel(promise.$$timeoutId); + } + return false; + }; + + return timeout; + }]; +} + +// NOTE: The usage of window and document instead of $window and $document here is +// deliberate. This service depends on the specific behavior of anchor nodes created by the +// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and +// cause us to break tests. In addition, when the browser resolves a URL for XHR, it +// doesn't know about mocked locations and resolves URLs to the real document - which is +// exactly the behavior needed here. There is little value is mocking these out for this +// service. +var urlParsingNode = document.createElement("a"); +var originUrl = urlResolve(window.location.href, true); + + +/** + * + * Implementation Notes for non-IE browsers + * ---------------------------------------- + * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, + * results both in the normalizing and parsing of the URL. Normalizing means that a relative + * URL will be resolved into an absolute URL in the context of the application document. + * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related + * properties are all populated to reflect the normalized URL. This approach has wide + * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See + * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html + * + * Implementation Notes for IE + * --------------------------- + * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other + * browsers. However, the parsed components will not be set if the URL assigned did not specify + * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We + * work around that by performing the parsing in a 2nd step by taking a previously normalized + * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the + * properties such as protocol, hostname, port, etc. + * + * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one + * uses the inner HTML approach to assign the URL as part of an HTML snippet - + * http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL. + * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception. + * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that + * method and IE < 8 is unsupported. + * + * References: + * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement + * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html + * http://url.spec.whatwg.org/#urlutils + * https://github.com/angular/angular.js/pull/2902 + * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ + * + * @kind function + * @param {string} url The URL to be parsed. + * @description Normalizes and parses a URL. + * @returns {object} Returns the normalized URL as a dictionary. + * + * | member name | Description | + * |---------------|----------------| + * | href | A normalized version of the provided URL if it was not an absolute URL | + * | protocol | The protocol including the trailing colon | + * | host | The host and port (if the port is non-default) of the normalizedUrl | + * | search | The search params, minus the question mark | + * | hash | The hash string, minus the hash symbol + * | hostname | The hostname + * | port | The port, without ":" + * | pathname | The pathname, beginning with "/" + * + */ +function urlResolve(url, base) { + var href = url; + + if (msie) { + // Normalize before parse. Refer Implementation Notes on why this is + // done in two steps on IE. + urlParsingNode.setAttribute("href", href); + href = urlParsingNode.href; + } + + urlParsingNode.setAttribute('href', href); + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + return { + href: urlParsingNode.href, + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', + host: urlParsingNode.host, + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', + hostname: urlParsingNode.hostname, + port: urlParsingNode.port, + pathname: (urlParsingNode.pathname.charAt(0) === '/') + ? urlParsingNode.pathname + : '/' + urlParsingNode.pathname + }; +} + +/** + * Parse a request URL and determine whether this is a same-origin request as the application document. + * + * @param {string|object} requestUrl The url of the request as a string that will be resolved + * or a parsed URL object. + * @returns {boolean} Whether the request is for the same origin as the application document. + */ +function urlIsSameOrigin(requestUrl) { + var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; + return (parsed.protocol === originUrl.protocol && + parsed.host === originUrl.host); +} + +/** + * @ngdoc service + * @name $window + * + * @description + * A reference to the browser's `window` object. While `window` + * is globally available in JavaScript, it causes testability problems, because + * it is a global variable. In angular we always refer to it through the + * `$window` service, so it may be overridden, removed or mocked for testing. + * + * Expressions, like the one defined for the `ngClick` directive in the example + * below, are evaluated with respect to the current scope. Therefore, there is + * no risk of inadvertently coding in a dependency on a global value in such an + * expression. + * + * @example + + + +
+ + +
+
+ + it('should display the greeting in the input box', function() { + element(by.model('greeting')).sendKeys('Hello, E2E Tests'); + // If we click the button it will block the test runner + // element(':button').click(); + }); + +
+ */ +function $WindowProvider(){ + this.$get = valueFn(window); +} + +/* global currencyFilter: true, + dateFilter: true, + filterFilter: true, + jsonFilter: true, + limitToFilter: true, + lowercaseFilter: true, + numberFilter: true, + orderByFilter: true, + uppercaseFilter: true, + */ + +/** + * @ngdoc provider + * @name $filterProvider + * @description + * + * Filters are just functions which transform input to an output. However filters need to be + * Dependency Injected. To achieve this a filter definition consists of a factory function which is + * annotated with dependencies and is responsible for creating a filter function. + * + * ```js + * // Filter registration + * function MyModule($provide, $filterProvider) { + * // create a service to demonstrate injection (not always needed) + * $provide.value('greet', function(name){ + * return 'Hello ' + name + '!'; + * }); + * + * // register a filter factory which uses the + * // greet service to demonstrate DI. + * $filterProvider.register('greet', function(greet){ + * // return the filter function which uses the greet service + * // to generate salutation + * return function(text) { + * // filters need to be forgiving so check input validity + * return text && greet(text) || text; + * }; + * }); + * } + * ``` + * + * The filter function is registered with the `$injector` under the filter name suffix with + * `Filter`. + * + * ```js + * it('should be the same instance', inject( + * function($filterProvider) { + * $filterProvider.register('reverse', function(){ + * return ...; + * }); + * }, + * function($filter, reverseFilter) { + * expect($filter('reverse')).toBe(reverseFilter); + * }); + * ``` + * + * + * For more information about how angular filters work, and how to create your own filters, see + * {@link guide/filter Filters} in the Angular Developer Guide. + */ + +/** + * @ngdoc service + * @name $filter + * @kind function + * @description + * Filters are used for formatting data displayed to the user. + * + * The general syntax in templates is as follows: + * + * {{ expression [| filter_name[:parameter_value] ... ] }} + * + * @param {String} name Name of the filter function to retrieve + * @return {Function} the filter function + * @example + + +
+

{{ originalText }}

+

{{ filteredText }}

+
+
+ + + angular.module('filterExample', []) + .controller('MainCtrl', function($scope, $filter) { + $scope.originalText = 'hello'; + $scope.filteredText = $filter('uppercase')($scope.originalText); + }); + +
+ */ +$FilterProvider.$inject = ['$provide']; +function $FilterProvider($provide) { + var suffix = 'Filter'; + + /** + * @ngdoc method + * @name $filterProvider#register + * @param {string|Object} name Name of the filter function, or an object map of filters where + * the keys are the filter names and the values are the filter factories. + * @returns {Object} Registered filter instance, or if a map of filters was provided then a map + * of the registered filter instances. + */ + function register(name, factory) { + if(isObject(name)) { + var filters = {}; + forEach(name, function(filter, key) { + filters[key] = register(key, filter); + }); + return filters; + } else { + return $provide.factory(name + suffix, factory); + } + } + this.register = register; + + this.$get = ['$injector', function($injector) { + return function(name) { + return $injector.get(name + suffix); + }; + }]; + + //////////////////////////////////////// + + /* global + currencyFilter: false, + dateFilter: false, + filterFilter: false, + jsonFilter: false, + limitToFilter: false, + lowercaseFilter: false, + numberFilter: false, + orderByFilter: false, + uppercaseFilter: false, + */ + + register('currency', currencyFilter); + register('date', dateFilter); + register('filter', filterFilter); + register('json', jsonFilter); + register('limitTo', limitToFilter); + register('lowercase', lowercaseFilter); + register('number', numberFilter); + register('orderBy', orderByFilter); + register('uppercase', uppercaseFilter); +} + +/** + * @ngdoc filter + * @name filter + * @kind function + * + * @description + * Selects a subset of items from `array` and returns it as a new array. + * + * @param {Array} array The source array. + * @param {string|Object|function()} expression The predicate to be used for selecting items from + * `array`. + * + * Can be one of: + * + * - `string`: The string is evaluated as an expression and the resulting value is used for substring match against + * the contents of the `array`. All strings or objects with string properties in `array` that contain this string + * will be returned. The predicate can be negated by prefixing the string with `!`. + * + * - `Object`: A pattern object can be used to filter specific properties on objects contained + * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items + * which have property `name` containing "M" and property `phone` containing "1". A special + * property name `$` can be used (as in `{$:"text"}`) to accept a match against any + * property of the object. That's equivalent to the simple substring match with a `string` + * as described above. The predicate can be negated by prefixing the string with `!`. + * For Example `{name: "!M"}` predicate will return an array of items which have property `name` + * not containing "M". + * + * - `function(value)`: A predicate function can be used to write arbitrary filters. The function is + * called for each element of `array`. The final result is an array of those elements that + * the predicate returned true for. + * + * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in + * determining if the expected value (from the filter expression) and actual value (from + * the object in the array) should be considered a match. + * + * Can be one of: + * + * - `function(actual, expected)`: + * The function will be given the object value and the predicate value to compare and + * should return true if the item should be included in filtered result. + * + * - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`. + * this is essentially strict comparison of expected and actual. + * + * - `false|undefined`: A short hand for a function which will look for a substring match in case + * insensitive way. + * + * @example + + +
+ + Search: + + + + + + +
NamePhone
{{friend.name}}{{friend.phone}}
+
+ Any:
+ Name only
+ Phone only
+ Equality
+ + + + + + +
NamePhone
{{friendObj.name}}{{friendObj.phone}}
+
+ + var expectFriendNames = function(expectedNames, key) { + element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) { + arr.forEach(function(wd, i) { + expect(wd.getText()).toMatch(expectedNames[i]); + }); + }); + }; + + it('should search across all fields when filtering with a string', function() { + var searchText = element(by.model('searchText')); + searchText.clear(); + searchText.sendKeys('m'); + expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend'); + + searchText.clear(); + searchText.sendKeys('76'); + expectFriendNames(['John', 'Julie'], 'friend'); + }); + + it('should search in specific fields when filtering with a predicate object', function() { + var searchAny = element(by.model('search.$')); + searchAny.clear(); + searchAny.sendKeys('i'); + expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj'); + }); + it('should use a equal comparison when comparator is true', function() { + var searchName = element(by.model('search.name')); + var strict = element(by.model('strict')); + searchName.clear(); + searchName.sendKeys('Julie'); + strict.click(); + expectFriendNames(['Julie'], 'friendObj'); + }); + +
+ */ +function filterFilter() { + return function(array, expression, comparator) { + if (!isArray(array)) return array; + + var comparatorType = typeof(comparator), + predicates = []; + + predicates.check = function(value) { + for (var j = 0; j < predicates.length; j++) { + if(!predicates[j](value)) { + return false; + } + } + return true; + }; + + if (comparatorType !== 'function') { + if (comparatorType === 'boolean' && comparator) { + comparator = function(obj, text) { + return angular.equals(obj, text); + }; + } else { + comparator = function(obj, text) { + if (obj && text && typeof obj === 'object' && typeof text === 'object') { + for (var objKey in obj) { + if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) && + comparator(obj[objKey], text[objKey])) { + return true; + } + } + return false; + } + text = (''+text).toLowerCase(); + return (''+obj).toLowerCase().indexOf(text) > -1; + }; + } + } + + var search = function(obj, text){ + if (typeof text == 'string' && text.charAt(0) === '!') { + return !search(obj, text.substr(1)); + } + switch (typeof obj) { + case "boolean": + case "number": + case "string": + return comparator(obj, text); + case "object": + switch (typeof text) { + case "object": + return comparator(obj, text); + default: + for ( var objKey in obj) { + if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { + return true; + } + } + break; + } + return false; + case "array": + for ( var i = 0; i < obj.length; i++) { + if (search(obj[i], text)) { + return true; + } + } + return false; + default: + return false; + } + }; + switch (typeof expression) { + case "boolean": + case "number": + case "string": + // Set up expression object and fall through + expression = {$:expression}; + // jshint -W086 + case "object": + // jshint +W086 + for (var key in expression) { + (function(path) { + if (typeof expression[path] === 'undefined') return; + predicates.push(function(value) { + return search(path == '$' ? value : (value && value[path]), expression[path]); + }); + })(key); + } + break; + case 'function': + predicates.push(expression); + break; + default: + return array; + } + var filtered = []; + for ( var j = 0; j < array.length; j++) { + var value = array[j]; + if (predicates.check(value)) { + filtered.push(value); + } + } + return filtered; + }; +} + +/** + * @ngdoc filter + * @name currency + * @kind function + * + * @description + * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default + * symbol for current locale is used. + * + * @param {number} amount Input to filter. + * @param {string=} symbol Currency symbol or identifier to be displayed. + * @returns {string} Formatted number. + * + * + * @example + + + +
+
+ default currency symbol ($): {{amount | currency}}
+ custom currency identifier (USD$): {{amount | currency:"USD$"}} +
+
+ + it('should init with 1234.56', function() { + expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); + expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56'); + }); + it('should update', function() { + if (browser.params.browser == 'safari') { + // Safari does not understand the minus key. See + // https://github.com/angular/protractor/issues/481 + return; + } + element(by.model('amount')).clear(); + element(by.model('amount')).sendKeys('-1234'); + expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)'); + expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('(USD$1,234.00)'); + }); + +
+ */ +currencyFilter.$inject = ['$locale']; +function currencyFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(amount, currencySymbol){ + if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM; + return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2). + replace(/\u00A4/g, currencySymbol); + }; +} + +/** + * @ngdoc filter + * @name number + * @kind function + * + * @description + * Formats a number as text. + * + * If the input is not a number an empty string is returned. + * + * @param {number|string} number Number to format. + * @param {(number|string)=} fractionSize Number of decimal places to round the number to. + * If this is not provided then the fraction size is computed from the current locale's number + * formatting pattern. In the case of the default locale, it will be 3. + * @returns {string} Number rounded to decimalPlaces and places a “,†after each third digit. + * + * @example + + + +
+ Enter number:
+ Default formatting: {{val | number}}
+ No fractions: {{val | number:0}}
+ Negative number: {{-val | number:4}} +
+
+ + it('should format numbers', function() { + expect(element(by.id('number-default')).getText()).toBe('1,234.568'); + expect(element(by.binding('val | number:0')).getText()).toBe('1,235'); + expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679'); + }); + + it('should update', function() { + element(by.model('val')).clear(); + element(by.model('val')).sendKeys('3374.333'); + expect(element(by.id('number-default')).getText()).toBe('3,374.333'); + expect(element(by.binding('val | number:0')).getText()).toBe('3,374'); + expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330'); + }); + +
+ */ + + +numberFilter.$inject = ['$locale']; +function numberFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(number, fractionSize) { + return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, + fractionSize); + }; +} + +var DECIMAL_SEP = '.'; +function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { + if (number == null || !isFinite(number) || isObject(number)) return ''; + + var isNegative = number < 0; + number = Math.abs(number); + var numStr = number + '', + formatedText = '', + parts = []; + + var hasExponent = false; + if (numStr.indexOf('e') !== -1) { + var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); + if (match && match[2] == '-' && match[3] > fractionSize + 1) { + numStr = '0'; + number = 0; + } else { + formatedText = numStr; + hasExponent = true; + } + } + + if (!hasExponent) { + var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; + + // determine fractionSize if it is not specified + if (isUndefined(fractionSize)) { + fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); + } + + // safely round numbers in JS without hitting imprecisions of floating-point arithmetics + // inspired by: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round + number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize); + + if (number === 0) { + isNegative = false; + } + + var fraction = ('' + number).split(DECIMAL_SEP); + var whole = fraction[0]; + fraction = fraction[1] || ''; + + var i, pos = 0, + lgroup = pattern.lgSize, + group = pattern.gSize; + + if (whole.length >= (lgroup + group)) { + pos = whole.length - lgroup; + for (i = 0; i < pos; i++) { + if ((pos - i)%group === 0 && i !== 0) { + formatedText += groupSep; + } + formatedText += whole.charAt(i); + } + } + + for (i = pos; i < whole.length; i++) { + if ((whole.length - i)%lgroup === 0 && i !== 0) { + formatedText += groupSep; + } + formatedText += whole.charAt(i); + } + + // format fraction part. + while(fraction.length < fractionSize) { + fraction += '0'; + } + + if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); + } else { + + if (fractionSize > 0 && number > -1 && number < 1) { + formatedText = number.toFixed(fractionSize); + } + } + + parts.push(isNegative ? pattern.negPre : pattern.posPre); + parts.push(formatedText); + parts.push(isNegative ? pattern.negSuf : pattern.posSuf); + return parts.join(''); +} + +function padNumber(num, digits, trim) { + var neg = ''; + if (num < 0) { + neg = '-'; + num = -num; + } + num = '' + num; + while(num.length < digits) num = '0' + num; + if (trim) + num = num.substr(num.length - digits); + return neg + num; +} + + +function dateGetter(name, size, offset, trim) { + offset = offset || 0; + return function(date) { + var value = date['get' + name](); + if (offset > 0 || value > -offset) + value += offset; + if (value === 0 && offset == -12 ) value = 12; + return padNumber(value, size, trim); + }; +} + +function dateStrGetter(name, shortForm) { + return function(date, formats) { + var value = date['get' + name](); + var get = uppercase(shortForm ? ('SHORT' + name) : name); + + return formats[get][value]; + }; +} + +function timeZoneGetter(date) { + var zone = -1 * date.getTimezoneOffset(); + var paddedZone = (zone >= 0) ? "+" : ""; + + paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + + padNumber(Math.abs(zone % 60), 2); + + return paddedZone; +} + +function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; +} + +var DATE_FORMATS = { + yyyy: dateGetter('FullYear', 4), + yy: dateGetter('FullYear', 2, 0, true), + y: dateGetter('FullYear', 1), + MMMM: dateStrGetter('Month'), + MMM: dateStrGetter('Month', true), + MM: dateGetter('Month', 2, 1), + M: dateGetter('Month', 1, 1), + dd: dateGetter('Date', 2), + d: dateGetter('Date', 1), + HH: dateGetter('Hours', 2), + H: dateGetter('Hours', 1), + hh: dateGetter('Hours', 2, -12), + h: dateGetter('Hours', 1, -12), + mm: dateGetter('Minutes', 2), + m: dateGetter('Minutes', 1), + ss: dateGetter('Seconds', 2), + s: dateGetter('Seconds', 1), + // while ISO 8601 requires fractions to be prefixed with `.` or `,` + // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions + sss: dateGetter('Milliseconds', 3), + EEEE: dateStrGetter('Day'), + EEE: dateStrGetter('Day', true), + a: ampmGetter, + Z: timeZoneGetter +}; + +var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, + NUMBER_STRING = /^\-?\d+$/; + +/** + * @ngdoc filter + * @name date + * @kind function + * + * @description + * Formats `date` to a string based on the requested `format`. + * + * `format` string can be composed of the following elements: + * + * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + * * `'MMMM'`: Month in year (January-December) + * * `'MMM'`: Month in year (Jan-Dec) + * * `'MM'`: Month in year, padded (01-12) + * * `'M'`: Month in year (1-12) + * * `'dd'`: Day in month, padded (01-31) + * * `'d'`: Day in month (1-31) + * * `'EEEE'`: Day in Week,(Sunday-Saturday) + * * `'EEE'`: Day in Week, (Sun-Sat) + * * `'HH'`: Hour in day, padded (00-23) + * * `'H'`: Hour in day (0-23) + * * `'hh'`: Hour in am/pm, padded (01-12) + * * `'h'`: Hour in am/pm, (1-12) + * * `'mm'`: Minute in hour, padded (00-59) + * * `'m'`: Minute in hour (0-59) + * * `'ss'`: Second in minute, padded (00-59) + * * `'s'`: Second in minute (0-59) + * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999) + * * `'a'`: am/pm marker + * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) + * + * `format` string can also be one of the following predefined + * {@link guide/i18n localizable formats}: + * + * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale + * (e.g. Sep 3, 2010 12:05:08 pm) + * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm) + * * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale + * (e.g. Friday, September 3, 2010) + * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010) + * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) + * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) + * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm) + * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm) + * + * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g. + * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence + * (e.g. `"h 'o''clock'"`). + * + * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or + * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its + * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is + * specified in the string input, the time is considered to be in the local timezone. + * @param {string=} format Formatting rules (see Description). If not specified, + * `mediumDate` is used. + * @returns {string} Formatted string or the input if input is not recognized as date/millis. + * + * @example + + + {{1288323623006 | date:'medium'}}: + {{1288323623006 | date:'medium'}}
+ {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}: + {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}
+ {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}: + {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}
+ {{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}: + {{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}
+
+ + it('should format date', function() { + expect(element(by.binding("1288323623006 | date:'medium'")).getText()). + toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); + expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()). + toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); + expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()). + toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); + expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()). + toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/); + }); + +
+ */ +dateFilter.$inject = ['$locale']; +function dateFilter($locale) { + + + var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; + // 1 2 3 4 5 6 7 8 9 10 11 + function jsonStringToDate(string) { + var match; + if (match = string.match(R_ISO8601_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0, + dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, + timeSetter = match[8] ? date.setUTCHours : date.setHours; + + if (match[9]) { + tzHour = int(match[9] + match[10]); + tzMin = int(match[9] + match[11]); + } + dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); + var h = int(match[4]||0) - tzHour; + var m = int(match[5]||0) - tzMin; + var s = int(match[6]||0); + var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000); + timeSetter.call(date, h, m, s, ms); + return date; + } + return string; + } + + + return function(date, format) { + var text = '', + parts = [], + fn, match; + + format = format || 'mediumDate'; + format = $locale.DATETIME_FORMATS[format] || format; + if (isString(date)) { + date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date); + } + + if (isNumber(date)) { + date = new Date(date); + } + + if (!isDate(date)) { + return date; + } + + while(format) { + match = DATE_FORMATS_SPLIT.exec(format); + if (match) { + parts = concat(parts, match, 1); + format = parts.pop(); + } else { + parts.push(format); + format = null; + } + } + + forEach(parts, function(value){ + fn = DATE_FORMATS[value]; + text += fn ? fn(date, $locale.DATETIME_FORMATS) + : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); + }); + + return text; + }; +} + + +/** + * @ngdoc filter + * @name json + * @kind function + * + * @description + * Allows you to convert a JavaScript object into JSON string. + * + * This filter is mostly useful for debugging. When using the double curly {{value}} notation + * the binding is automatically converted to JSON. + * + * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. + * @returns {string} JSON string. + * + * + * @example + + +
{{ {'name':'value'} | json }}
+
+ + it('should jsonify filtered objects', function() { + expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n "name": ?"value"\n}/); + }); + +
+ * + */ +function jsonFilter() { + return function(object) { + return toJson(object, true); + }; +} + + +/** + * @ngdoc filter + * @name lowercase + * @kind function + * @description + * Converts string to lowercase. + * @see angular.lowercase + */ +var lowercaseFilter = valueFn(lowercase); + + +/** + * @ngdoc filter + * @name uppercase + * @kind function + * @description + * Converts string to uppercase. + * @see angular.uppercase + */ +var uppercaseFilter = valueFn(uppercase); + +/** + * @ngdoc filter + * @name limitTo + * @kind function + * + * @description + * Creates a new array or string containing only a specified number of elements. The elements + * are taken from either the beginning or the end of the source array or string, as specified by + * the value and sign (positive or negative) of `limit`. + * + * @param {Array|string} input Source array or string to be limited. + * @param {string|number} limit The length of the returned array or string. If the `limit` number + * is positive, `limit` number of items from the beginning of the source array/string are copied. + * If the number is negative, `limit` number of items from the end of the source array/string + * are copied. The `limit` will be trimmed if it exceeds `array.length` + * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array + * had less than `limit` elements. + * + * @example + + + +
+ Limit {{numbers}} to: +

Output numbers: {{ numbers | limitTo:numLimit }}

+ Limit {{letters}} to: +

Output letters: {{ letters | limitTo:letterLimit }}

+
+
+ + var numLimitInput = element(by.model('numLimit')); + var letterLimitInput = element(by.model('letterLimit')); + var limitedNumbers = element(by.binding('numbers | limitTo:numLimit')); + var limitedLetters = element(by.binding('letters | limitTo:letterLimit')); + + it('should limit the number array to first three items', function() { + expect(numLimitInput.getAttribute('value')).toBe('3'); + expect(letterLimitInput.getAttribute('value')).toBe('3'); + expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]'); + expect(limitedLetters.getText()).toEqual('Output letters: abc'); + }); + + it('should update the output when -3 is entered', function() { + numLimitInput.clear(); + numLimitInput.sendKeys('-3'); + letterLimitInput.clear(); + letterLimitInput.sendKeys('-3'); + expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); + expect(limitedLetters.getText()).toEqual('Output letters: ghi'); + }); + + it('should not exceed the maximum size of input array', function() { + numLimitInput.clear(); + numLimitInput.sendKeys('100'); + letterLimitInput.clear(); + letterLimitInput.sendKeys('100'); + expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]'); + expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi'); + }); + +
+ */ +function limitToFilter(){ + return function(input, limit) { + if (!isArray(input) && !isString(input)) return input; + + if (Math.abs(Number(limit)) === Infinity) { + limit = Number(limit); + } else { + limit = int(limit); + } + + if (isString(input)) { + //NaN check on limit + if (limit) { + return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length); + } else { + return ""; + } + } + + var out = [], + i, n; + + // if abs(limit) exceeds maximum length, trim it + if (limit > input.length) + limit = input.length; + else if (limit < -input.length) + limit = -input.length; + + if (limit > 0) { + i = 0; + n = limit; + } else { + i = input.length + limit; + n = input.length; + } + + for (; i} expression A predicate to be + * used by the comparator to determine the order of elements. + * + * Can be one of: + * + * - `function`: Getter function. The result of this function will be sorted using the + * `<`, `=`, `>` operator. + * - `string`: An Angular expression. The result of this expression is used to compare elements + * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by + * 3 first characters of a property called `name`). The result of a constant expression + * is interpreted as a property name to be used in comparisons (for example `"special name"` + * to sort object by the value of their `special name` property). An expression can be + * optionally prefixed with `+` or `-` to control ascending or descending sort order + * (for example, `+name` or `-name`). + * - `Array`: An array of function or string predicates. The first predicate in the array + * is used for sorting, but when two items are equivalent, the next predicate is used. + * + * @param {boolean=} reverse Reverse the order of the array. + * @returns {Array} Sorted copy of the source array. + * + * @example + + + +
+
Sorting predicate = {{predicate}}; reverse = {{reverse}}
+
+ [ unsorted ] + + + + + + + + + + + +
Name + (^)Phone NumberAge
{{friend.name}}{{friend.phone}}{{friend.age}}
+
+
+
+ * + * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the + * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the + * desired parameters. + * + * Example: + * + * @example + + +
+ + + + + + + + + + + +
Name + (^)Phone NumberAge
{{friend.name}}{{friend.phone}}{{friend.age}}
+
+
+ + + angular.module('orderByExample', []) + .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) { + var orderBy = $filter('orderBy'); + $scope.friends = [ + { name: 'John', phone: '555-1212', age: 10 }, + { name: 'Mary', phone: '555-9876', age: 19 }, + { name: 'Mike', phone: '555-4321', age: 21 }, + { name: 'Adam', phone: '555-5678', age: 35 }, + { name: 'Julie', phone: '555-8765', age: 29 } + ]; + $scope.order = function(predicate, reverse) { + $scope.friends = orderBy($scope.friends, predicate, reverse); + }; + $scope.order('-age',false); + }]); + +
+ */ +orderByFilter.$inject = ['$parse']; +function orderByFilter($parse){ + return function(array, sortPredicate, reverseOrder) { + if (!(isArrayLike(array))) return array; + if (!sortPredicate) return array; + sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; + sortPredicate = map(sortPredicate, function(predicate){ + var descending = false, get = predicate || identity; + if (isString(predicate)) { + if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { + descending = predicate.charAt(0) == '-'; + predicate = predicate.substring(1); + } + get = $parse(predicate); + if (get.constant) { + var key = get(); + return reverseComparator(function(a,b) { + return compare(a[key], b[key]); + }, descending); + } + } + return reverseComparator(function(a,b){ + return compare(get(a),get(b)); + }, descending); + }); + var arrayCopy = []; + for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); } + return arrayCopy.sort(reverseComparator(comparator, reverseOrder)); + + function comparator(o1, o2){ + for ( var i = 0; i < sortPredicate.length; i++) { + var comp = sortPredicate[i](o1, o2); + if (comp !== 0) return comp; + } + return 0; + } + function reverseComparator(comp, descending) { + return toBoolean(descending) + ? function(a,b){return comp(b,a);} + : comp; + } + function compare(v1, v2){ + var t1 = typeof v1; + var t2 = typeof v2; + if (t1 == t2) { + if (isDate(v1) && isDate(v2)) { + v1 = v1.valueOf(); + v2 = v2.valueOf(); + } + if (t1 == "string") { + v1 = v1.toLowerCase(); + v2 = v2.toLowerCase(); + } + if (v1 === v2) return 0; + return v1 < v2 ? -1 : 1; + } else { + return t1 < t2 ? -1 : 1; + } + } + }; +} + +function ngDirective(directive) { + if (isFunction(directive)) { + directive = { + link: directive + }; + } + directive.restrict = directive.restrict || 'AC'; + return valueFn(directive); +} + +/** + * @ngdoc directive + * @name a + * @restrict E + * + * @description + * Modifies the default behavior of the html A tag so that the default action is prevented when + * the href attribute is empty. + * + * This change permits the easy creation of action links with the `ngClick` directive + * without changing the location or causing page reloads, e.g.: + * `Add Item` + */ +var htmlAnchorDirective = valueFn({ + restrict: 'E', + compile: function(element, attr) { + + if (msie <= 8) { + + // turn link into a stylable link in IE + // but only if it doesn't have name attribute, in which case it's an anchor + if (!attr.href && !attr.name) { + attr.$set('href', ''); + } + + // add a comment node to anchors to workaround IE bug that causes element content to be reset + // to new attribute content if attribute is updated with value containing @ and element also + // contains value with @ + // see issue #1949 + element.append(document.createComment('IE fix')); + } + + if (!attr.href && !attr.xlinkHref && !attr.name) { + return function(scope, element) { + // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. + var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? + 'xlink:href' : 'href'; + element.on('click', function(event){ + // if we have no href url, then don't navigate anywhere. + if (!element.attr(href)) { + event.preventDefault(); + } + }); + }; + } + } +}); + +/** + * @ngdoc directive + * @name ngHref + * @restrict A + * @priority 99 + * + * @description + * Using Angular markup like `{{hash}}` in an href attribute will + * make the link go to the wrong URL if the user clicks it before + * Angular has a chance to replace the `{{hash}}` markup with its + * value. Until Angular replaces the markup the link will be broken + * and will most likely return a 404 error. + * + * The `ngHref` directive solves this problem. + * + * The wrong way to write it: + * ```html + * + * ``` + * + * The correct way to write it: + * ```html + * + * ``` + * + * @element A + * @param {template} ngHref any string which can contain `{{}}` markup. + * + * @example + * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes + * in links and their different behaviors: + + +
+
link 1 (link, don't reload)
+ link 2 (link, don't reload)
+ link 3 (link, reload!)
+ anchor (link, don't reload)
+ anchor (no link)
+ link (link, change location) +
+ + it('should execute ng-click but not reload when href without value', function() { + element(by.id('link-1')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('1'); + expect(element(by.id('link-1')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click but not reload when href empty string', function() { + element(by.id('link-2')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('2'); + expect(element(by.id('link-2')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click and change url when ng-href specified', function() { + expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/); + + element(by.id('link-3')).click(); + + // At this point, we navigate away from an Angular page, so we need + // to use browser.driver to get the base webdriver. + + browser.wait(function() { + return browser.driver.getCurrentUrl().then(function(url) { + return url.match(/\/123$/); + }); + }, 5000, 'page should navigate to /123'); + }); + + xit('should execute ng-click but not reload when href empty string and name specified', function() { + element(by.id('link-4')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('4'); + expect(element(by.id('link-4')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click but not reload when no href but name specified', function() { + element(by.id('link-5')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('5'); + expect(element(by.id('link-5')).getAttribute('href')).toBe(null); + }); + + it('should only change url when only ng-href', function() { + element(by.model('value')).clear(); + element(by.model('value')).sendKeys('6'); + expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/); + + element(by.id('link-6')).click(); + + // At this point, we navigate away from an Angular page, so we need + // to use browser.driver to get the base webdriver. + browser.wait(function() { + return browser.driver.getCurrentUrl().then(function(url) { + return url.match(/\/6$/); + }); + }, 5000, 'page should navigate to /6'); + }); + + + */ + +/** + * @ngdoc directive + * @name ngSrc + * @restrict A + * @priority 99 + * + * @description + * Using Angular markup like `{{hash}}` in a `src` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrc` directive solves this problem. + * + * The buggy way to write it: + * ```html + * + * ``` + * + * The correct way to write it: + * ```html + * + * ``` + * + * @element IMG + * @param {template} ngSrc any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ngSrcset + * @restrict A + * @priority 99 + * + * @description + * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrcset` directive solves this problem. + * + * The buggy way to write it: + * ```html + * + * ``` + * + * The correct way to write it: + * ```html + * + * ``` + * + * @element IMG + * @param {template} ngSrcset any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ngDisabled + * @restrict A + * @priority 100 + * + * @description + * + * We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs: + * ```html + *
+ * + *
+ * ``` + * + * The HTML specification does not require browsers to preserve the values of boolean attributes + * such as disabled. (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * The `ngDisabled` directive solves this problem for the `disabled` attribute. + * This complementary directive is not removed by the browser and so provides + * a permanent reliable place to store the binding information. + * + * @example + + + Click me to toggle:
+ +
+ + it('should toggle button', function() { + expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy(); + element(by.model('checked')).click(); + expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy(); + }); + +
+ * + * @element INPUT + * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy, + * then special attribute "disabled" will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngChecked + * @restrict A + * @priority 100 + * + * @description + * The HTML specification does not require browsers to preserve the values of boolean attributes + * such as checked. (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * The `ngChecked` directive solves this problem for the `checked` attribute. + * This complementary directive is not removed by the browser and so provides + * a permanent reliable place to store the binding information. + * @example + + + Check me to check both:
+ +
+ + it('should check both checkBoxes', function() { + expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy(); + element(by.model('master')).click(); + expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy(); + }); + +
+ * + * @element INPUT + * @param {expression} ngChecked If the {@link guide/expression expression} is truthy, + * then special attribute "checked" will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngReadonly + * @restrict A + * @priority 100 + * + * @description + * The HTML specification does not require browsers to preserve the values of boolean attributes + * such as readonly. (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * The `ngReadonly` directive solves this problem for the `readonly` attribute. + * This complementary directive is not removed by the browser and so provides + * a permanent reliable place to store the binding information. + * @example + + + Check me to make text readonly:
+ +
+ + it('should toggle readonly attr', function() { + expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy(); + element(by.model('checked')).click(); + expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy(); + }); + +
+ * + * @element INPUT + * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy, + * then special attribute "readonly" will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngSelected + * @restrict A + * @priority 100 + * + * @description + * The HTML specification does not require browsers to preserve the values of boolean attributes + * such as selected. (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * The `ngSelected` directive solves this problem for the `selected` attribute. + * This complementary directive is not removed by the browser and so provides + * a permanent reliable place to store the binding information. + * + * @example + + + Check me to select:
+ +
+ + it('should select Greetings!', function() { + expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy(); + element(by.model('selected')).click(); + expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy(); + }); + +
+ * + * @element OPTION + * @param {expression} ngSelected If the {@link guide/expression expression} is truthy, + * then special attribute "selected" will be set on the element + */ + +/** + * @ngdoc directive + * @name ngOpen + * @restrict A + * @priority 100 + * + * @description + * The HTML specification does not require browsers to preserve the values of boolean attributes + * such as open. (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * The `ngOpen` directive solves this problem for the `open` attribute. + * This complementary directive is not removed by the browser and so provides + * a permanent reliable place to store the binding information. + * @example + + + Check me check multiple:
+
+ Show/Hide me +
+
+ + it('should toggle open', function() { + expect(element(by.id('details')).getAttribute('open')).toBeFalsy(); + element(by.model('open')).click(); + expect(element(by.id('details')).getAttribute('open')).toBeTruthy(); + }); + +
+ * + * @element DETAILS + * @param {expression} ngOpen If the {@link guide/expression expression} is truthy, + * then special attribute "open" will be set on the element + */ + +var ngAttributeAliasDirectives = {}; + + +// boolean attrs are evaluated +forEach(BOOLEAN_ATTR, function(propName, attrName) { + // binding to multiple is not supported + if (propName == "multiple") return; + + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + priority: 100, + link: function(scope, element, attr) { + scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { + attr.$set(attrName, !!value); + }); + } + }; + }; +}); + + +// ng-src, ng-srcset, ng-href are interpolated +forEach(['src', 'srcset', 'href'], function(attrName) { + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + priority: 99, // it needs to run after the attributes are interpolated + link: function(scope, element, attr) { + var propName = attrName, + name = attrName; + + if (attrName === 'href' && + toString.call(element.prop('href')) === '[object SVGAnimatedString]') { + name = 'xlinkHref'; + attr.$attr[name] = 'xlink:href'; + propName = null; + } + + attr.$observe(normalized, function(value) { + if (!value) { + if (attrName === 'href') { + attr.$set(name, null); + } + return; + } + + attr.$set(name, value); + + // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect. + // we use attr[attrName] value since $set can sanitize the url. + if (msie && propName) element.prop(propName, attr[name]); + }); + } + }; + }; +}); + +/* global -nullFormCtrl */ +var nullFormCtrl = { + $addControl: noop, + $removeControl: noop, + $setValidity: noop, + $setDirty: noop, + $setPristine: noop +}; + +/** + * @ngdoc type + * @name form.FormController + * + * @property {boolean} $pristine True if user has not interacted with the form yet. + * @property {boolean} $dirty True if user has already interacted with the form. + * @property {boolean} $valid True if all of the containing forms and controls are valid. + * @property {boolean} $invalid True if at least one containing control or form is invalid. + * + * @property {Object} $error Is an object hash, containing references to all invalid controls or + * forms, where: + * + * - keys are validation tokens (error names), + * - values are arrays of controls or forms that are invalid for given error name. + * + * + * Built-in validation tokens: + * + * - `email` + * - `max` + * - `maxlength` + * - `min` + * - `minlength` + * - `number` + * - `pattern` + * - `required` + * - `url` + * + * @description + * `FormController` keeps track of all its controls and nested forms as well as the state of them, + * such as being valid/invalid or dirty/pristine. + * + * Each {@link ng.directive:form form} directive creates an instance + * of `FormController`. + * + */ +//asks for $scope to fool the BC controller module +FormController.$inject = ['$element', '$attrs', '$scope', '$animate']; +function FormController(element, attrs, $scope, $animate) { + var form = this, + parentForm = element.parent().controller('form') || nullFormCtrl, + invalidCount = 0, // used to easily determine if we are valid + errors = form.$error = {}, + controls = []; + + // init state + form.$name = attrs.name || attrs.ngForm; + form.$dirty = false; + form.$pristine = true; + form.$valid = true; + form.$invalid = false; + + parentForm.$addControl(form); + + // Setup initial state of the control + element.addClass(PRISTINE_CLASS); + toggleValidCss(true); + + // convenience method for easy toggling of classes + function toggleValidCss(isValid, validationErrorKey) { + validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; + $animate.setClass(element, + (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey, + (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey); + } + + /** + * @ngdoc method + * @name form.FormController#$addControl + * + * @description + * Register a control with the form. + * + * Input elements using ngModelController do this automatically when they are linked. + */ + form.$addControl = function(control) { + // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored + // and not added to the scope. Now we throw an error. + assertNotHasOwnProperty(control.$name, 'input'); + controls.push(control); + + if (control.$name) { + form[control.$name] = control; + } + }; + + /** + * @ngdoc method + * @name form.FormController#$removeControl + * + * @description + * Deregister a control from the form. + * + * Input elements using ngModelController do this automatically when they are destroyed. + */ + form.$removeControl = function(control) { + if (control.$name && form[control.$name] === control) { + delete form[control.$name]; + } + forEach(errors, function(queue, validationToken) { + form.$setValidity(validationToken, true, control); + }); + + arrayRemove(controls, control); + }; + + /** + * @ngdoc method + * @name form.FormController#$setValidity + * + * @description + * Sets the validity of a form control. + * + * This method will also propagate to parent forms. + */ + form.$setValidity = function(validationToken, isValid, control) { + var queue = errors[validationToken]; + + if (isValid) { + if (queue) { + arrayRemove(queue, control); + if (!queue.length) { + invalidCount--; + if (!invalidCount) { + toggleValidCss(isValid); + form.$valid = true; + form.$invalid = false; + } + errors[validationToken] = false; + toggleValidCss(true, validationToken); + parentForm.$setValidity(validationToken, true, form); + } + } + + } else { + if (!invalidCount) { + toggleValidCss(isValid); + } + if (queue) { + if (includes(queue, control)) return; + } else { + errors[validationToken] = queue = []; + invalidCount++; + toggleValidCss(false, validationToken); + parentForm.$setValidity(validationToken, false, form); + } + queue.push(control); + + form.$valid = false; + form.$invalid = true; + } + }; + + /** + * @ngdoc method + * @name form.FormController#$setDirty + * + * @description + * Sets the form to a dirty state. + * + * This method can be called to add the 'ng-dirty' class and set the form to a dirty + * state (ng-dirty class). This method will also propagate to parent forms. + */ + form.$setDirty = function() { + $animate.removeClass(element, PRISTINE_CLASS); + $animate.addClass(element, DIRTY_CLASS); + form.$dirty = true; + form.$pristine = false; + parentForm.$setDirty(); + }; + + /** + * @ngdoc method + * @name form.FormController#$setPristine + * + * @description + * Sets the form to its pristine state. + * + * This method can be called to remove the 'ng-dirty' class and set the form to its pristine + * state (ng-pristine class). This method will also propagate to all the controls contained + * in this form. + * + * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after + * saving or resetting it. + */ + form.$setPristine = function () { + $animate.removeClass(element, DIRTY_CLASS); + $animate.addClass(element, PRISTINE_CLASS); + form.$dirty = false; + form.$pristine = true; + forEach(controls, function(control) { + control.$setPristine(); + }); + }; +} + + +/** + * @ngdoc directive + * @name ngForm + * @restrict EAC + * + * @description + * Nestable alias of {@link ng.directive:form `form`} directive. HTML + * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a + * sub-group of controls needs to be determined. + * + * Note: the purpose of `ngForm` is to group controls, + * but not to be a replacement for the `
` tag with all of its capabilities + * (e.g. posting to the server, ...). + * + * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + * + */ + + /** + * @ngdoc directive + * @name form + * @restrict E + * + * @description + * Directive that instantiates + * {@link form.FormController FormController}. + * + * If the `name` attribute is specified, the form controller is published onto the current scope under + * this name. + * + * # Alias: {@link ng.directive:ngForm `ngForm`} + * + * In Angular forms can be nested. This means that the outer form is valid when all of the child + * forms are valid as well. However, browsers do not allow nesting of `` elements, so + * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to + * `` but can be nested. This allows you to have nested forms, which is very useful when + * using Angular validation directives in forms that are dynamically generated using the + * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name` + * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an + * `ngForm` directive and nest these in an outer `form` element. + * + * + * # CSS classes + * - `ng-valid` is set if the form is valid. + * - `ng-invalid` is set if the form is invalid. + * - `ng-pristine` is set if the form is pristine. + * - `ng-dirty` is set if the form is dirty. + * + * Keep in mind that ngAnimate can detect each of these classes when added and removed. + * + * + * # Submitting a form and preventing the default action + * + * Since the role of forms in client-side Angular applications is different than in classical + * roundtrip apps, it is desirable for the browser not to translate the form submission into a full + * page reload that sends the data to the server. Instead some javascript logic should be triggered + * to handle the form submission in an application-specific way. + * + * For this reason, Angular prevents the default action (form submission to the server) unless the + * `` element has an `action` attribute specified. + * + * You can use one of the following two ways to specify what javascript method should be called when + * a form is submitted: + * + * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element + * - {@link ng.directive:ngClick ngClick} directive on the first + * button or input field of type submit (input[type=submit]) + * + * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit} + * or {@link ng.directive:ngClick ngClick} directives. + * This is because of the following form submission rules in the HTML specification: + * + * - If a form has only one input field then hitting enter in this field triggers form submit + * (`ngSubmit`) + * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter + * doesn't trigger submit + * - if a form has one or more input fields and one or more buttons or input[type=submit] then + * hitting enter in any of the input fields will trigger the click handler on the *first* button or + * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) + * + * + * ## Animation Hooks + * + * Animations in ngForm are triggered when any of the associated CSS classes are added and removed. + * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any + * other validations that are performed within the form. Animations in ngForm are similar to how + * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well + * as JS animations. + * + * The following example shows a simple way to utilize CSS transitions to style a form element + * that has been rendered as invalid after it has been validated: + * + *
+ * //be sure to include ngAnimate as a module to hook into more
+ * //advanced animations
+ * .my-form {
+ *   transition:0.5s linear all;
+ *   background: white;
+ * }
+ * .my-form.ng-invalid {
+ *   background: red;
+ *   color:white;
+ * }
+ * 
+ * + * @example + + + + + + userType: + Required!
+ userType = {{userType}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+ +
+ + it('should initialize to model', function() { + var userType = element(by.binding('userType')); + var valid = element(by.binding('myForm.input.$valid')); + + expect(userType.getText()).toContain('guest'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + var userType = element(by.binding('userType')); + var valid = element(by.binding('myForm.input.$valid')); + var userInput = element(by.model('userType')); + + userInput.clear(); + userInput.sendKeys(''); + + expect(userType.getText()).toEqual('userType ='); + expect(valid.getText()).toContain('false'); + }); + +
+ * + * @param {string=} name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + */ +var formDirectiveFactory = function(isNgForm) { + return ['$timeout', function($timeout) { + var formDirective = { + name: 'form', + restrict: isNgForm ? 'EAC' : 'E', + controller: FormController, + compile: function() { + return { + pre: function(scope, formElement, attr, controller) { + if (!attr.action) { + // we can't use jq events because if a form is destroyed during submission the default + // action is not prevented. see #1238 + // + // IE 9 is not affected because it doesn't fire a submit event and try to do a full + // page reload if the form was destroyed by submission of the form via a click handler + // on a button in the form. Looks like an IE9 specific bug. + var preventDefaultListener = function(event) { + event.preventDefault + ? event.preventDefault() + : event.returnValue = false; // IE + }; + + addEventListenerFn(formElement[0], 'submit', preventDefaultListener); + + // unregister the preventDefault listener so that we don't not leak memory but in a + // way that will achieve the prevention of the default action. + formElement.on('$destroy', function() { + $timeout(function() { + removeEventListenerFn(formElement[0], 'submit', preventDefaultListener); + }, 0, false); + }); + } + + var parentFormCtrl = formElement.parent().controller('form'), + alias = attr.name || attr.ngForm; + + if (alias) { + setter(scope, alias, controller, alias); + } + if (parentFormCtrl) { + formElement.on('$destroy', function() { + parentFormCtrl.$removeControl(controller); + if (alias) { + setter(scope, alias, undefined, alias); + } + extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards + }); + } + } + }; + } + }; + + return formDirective; + }]; +}; + +var formDirective = formDirectiveFactory(); +var ngFormDirective = formDirectiveFactory(true); + +/* global VALID_CLASS: true, + INVALID_CLASS: true, + PRISTINE_CLASS: true, + DIRTY_CLASS: true +*/ + +var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; +var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; +var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; + +var inputType = { + + /** + * @ngdoc input + * @name input[text] + * + * @description + * Standard HTML text input with angular data binding, inherited by most of the `input` elements. + * + * *NOTE* Not every feature offered is available for all input types. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Adds `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. + * This parameter is ignored for input[type=password] controls, which will never trim the + * input. + * + * @example + + + +
+ Single word: + + Required! + + Single word only! + + text = {{text}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+
+
+ + var text = element(by.binding('text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('guest'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if multi word', function() { + input.clear(); + input.sendKeys('hello world'); + + expect(valid.getText()).toContain('false'); + }); + +
+ */ + 'text': textInputType, + + + /** + * @ngdoc input + * @name input[number] + * + * @description + * Text input with number validation and transformation. Sets the `number` validation + * error if not a valid number. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
+ Number: + + Required! + + Not valid number! + value = {{value}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+
+
+ + var value = element(by.binding('value')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('value')); + + it('should initialize to model', function() { + expect(value.getText()).toContain('12'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if over max', function() { + input.clear(); + input.sendKeys('123'); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('false'); + }); + +
+ */ + 'number': numberInputType, + + + /** + * @ngdoc input + * @name input[url] + * + * @description + * Text input with URL validation. Sets the `url` validation error key if the content is not a + * valid URL. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
+ URL: + + Required! + + Not valid url! + text = {{text}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+ myForm.$error.url = {{!!myForm.$error.url}}
+
+
+ + var text = element(by.binding('text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('http://google.com'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if not url', function() { + input.clear(); + input.sendKeys('box'); + + expect(valid.getText()).toContain('false'); + }); + +
+ */ + 'url': urlInputType, + + + /** + * @ngdoc input + * @name input[email] + * + * @description + * Text input with email validation. Sets the `email` validation error key if not a valid email + * address. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
+ Email: + + Required! + + Not valid email! + text = {{text}}
+ myForm.input.$valid = {{myForm.input.$valid}}
+ myForm.input.$error = {{myForm.input.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+ myForm.$error.email = {{!!myForm.$error.email}}
+
+
+ + var text = element(by.binding('text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('me@example.com'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if not email', function() { + input.clear(); + input.sendKeys('xxx'); + + expect(valid.getText()).toContain('false'); + }); + +
+ */ + 'email': emailInputType, + + + /** + * @ngdoc input + * @name input[radio] + * + * @description + * HTML radio button. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string} value The value to which the expression should be set when selected. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {string} ngValue Angular expression which sets the value to which the expression should + * be set when selected. + * + * @example + + + +
+ Red
+ Green
+ Blue
+ color = {{color | json}}
+
+ Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`. +
+ + it('should change state', function() { + var color = element(by.binding('color')); + + expect(color.getText()).toContain('blue'); + + element.all(by.model('color')).get(0).click(); + + expect(color.getText()).toContain('red'); + }); + +
+ */ + 'radio': radioInputType, + + + /** + * @ngdoc input + * @name input[checkbox] + * + * @description + * HTML checkbox. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngTrueValue The value to which the expression should be set when selected. + * @param {string=} ngFalseValue The value to which the expression should be set when not selected. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
+ Value1:
+ Value2:
+ value1 = {{value1}}
+ value2 = {{value2}}
+
+
+ + it('should change state', function() { + var value1 = element(by.binding('value1')); + var value2 = element(by.binding('value2')); + + expect(value1.getText()).toContain('true'); + expect(value2.getText()).toContain('YES'); + + element(by.model('value1')).click(); + element(by.model('value2')).click(); + + expect(value1.getText()).toContain('false'); + expect(value2.getText()).toContain('NO'); + }); + +
+ */ + 'checkbox': checkboxInputType, + + 'hidden': noop, + 'button': noop, + 'submit': noop, + 'reset': noop, + 'file': noop +}; + +// A helper function to call $setValidity and return the value / undefined, +// a pattern that is repeated a lot in the input validation logic. +function validate(ctrl, validatorName, validity, value){ + ctrl.$setValidity(validatorName, validity); + return validity ? value : undefined; +} + +function testFlags(validity, flags) { + var i, flag; + if (flags) { + for (i=0; i= minlength, value); + }; + + ctrl.$parsers.push(minLengthValidator); + ctrl.$formatters.push(minLengthValidator); + } + + // max length validator + if (attr.ngMaxlength) { + var maxlength = int(attr.ngMaxlength); + var maxLengthValidator = function(value) { + return validate(ctrl, 'maxlength', ctrl.$isEmpty(value) || value.length <= maxlength, value); + }; + + ctrl.$parsers.push(maxLengthValidator); + ctrl.$formatters.push(maxLengthValidator); + } +} + +var numberBadFlags = ['badInput']; + +function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); + + ctrl.$parsers.push(function(value) { + var empty = ctrl.$isEmpty(value); + if (empty || NUMBER_REGEXP.test(value)) { + ctrl.$setValidity('number', true); + return value === '' ? null : (empty ? value : parseFloat(value)); + } else { + ctrl.$setValidity('number', false); + return undefined; + } + }); + + addNativeHtml5Validators(ctrl, 'number', numberBadFlags, null, ctrl.$$validityState); + + ctrl.$formatters.push(function(value) { + return ctrl.$isEmpty(value) ? '' : '' + value; + }); + + if (attr.min) { + var minValidator = function(value) { + var min = parseFloat(attr.min); + return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value); + }; + + ctrl.$parsers.push(minValidator); + ctrl.$formatters.push(minValidator); + } + + if (attr.max) { + var maxValidator = function(value) { + var max = parseFloat(attr.max); + return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value); + }; + + ctrl.$parsers.push(maxValidator); + ctrl.$formatters.push(maxValidator); + } + + ctrl.$formatters.push(function(value) { + return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value); + }); +} + +function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); + + var urlValidator = function(value) { + return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value); + }; + + ctrl.$formatters.push(urlValidator); + ctrl.$parsers.push(urlValidator); +} + +function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { + textInputType(scope, element, attr, ctrl, $sniffer, $browser); + + var emailValidator = function(value) { + return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value); + }; + + ctrl.$formatters.push(emailValidator); + ctrl.$parsers.push(emailValidator); +} + +function radioInputType(scope, element, attr, ctrl) { + // make the name unique, if not defined + if (isUndefined(attr.name)) { + element.attr('name', nextUid()); + } + + element.on('click', function() { + if (element[0].checked) { + scope.$apply(function() { + ctrl.$setViewValue(attr.value); + }); + } + }); + + ctrl.$render = function() { + var value = attr.value; + element[0].checked = (value == ctrl.$viewValue); + }; + + attr.$observe('value', ctrl.$render); +} + +function checkboxInputType(scope, element, attr, ctrl) { + var trueValue = attr.ngTrueValue, + falseValue = attr.ngFalseValue; + + if (!isString(trueValue)) trueValue = true; + if (!isString(falseValue)) falseValue = false; + + element.on('click', function() { + scope.$apply(function() { + ctrl.$setViewValue(element[0].checked); + }); + }); + + ctrl.$render = function() { + element[0].checked = ctrl.$viewValue; + }; + + // Override the standard `$isEmpty` because a value of `false` means empty in a checkbox. + ctrl.$isEmpty = function(value) { + return value !== trueValue; + }; + + ctrl.$formatters.push(function(value) { + return value === trueValue; + }); + + ctrl.$parsers.push(function(value) { + return value ? trueValue : falseValue; + }); +} + + +/** + * @ngdoc directive + * @name textarea + * @restrict E + * + * @description + * HTML textarea element control with angular data-binding. The data-binding and validation + * properties of this element are exactly the same as those of the + * {@link ng.directive:input input element}. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. + */ + + +/** + * @ngdoc directive + * @name input + * @restrict E + * + * @description + * HTML input element control with angular data-binding. Input control follows HTML5 input types + * and polyfills the HTML5 validation behavior for older browsers. + * + * *NOTE* Not every feature offered is available for all input types. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {boolean=} ngRequired Sets `required` attribute if set to true + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. + * This parameter is ignored for input[type=password] controls, which will never trim the + * input. + * + * @example + + + +
+
+ User name: + + Required!
+ Last name: + + Too short! + + Too long!
+
+
+ user = {{user}}
+ myForm.userName.$valid = {{myForm.userName.$valid}}
+ myForm.userName.$error = {{myForm.userName.$error}}
+ myForm.lastName.$valid = {{myForm.lastName.$valid}}
+ myForm.lastName.$error = {{myForm.lastName.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+ myForm.$error.minlength = {{!!myForm.$error.minlength}}
+ myForm.$error.maxlength = {{!!myForm.$error.maxlength}}
+
+
+ + var user = element(by.binding('{{user}}')); + var userNameValid = element(by.binding('myForm.userName.$valid')); + var lastNameValid = element(by.binding('myForm.lastName.$valid')); + var lastNameError = element(by.binding('myForm.lastName.$error')); + var formValid = element(by.binding('myForm.$valid')); + var userNameInput = element(by.model('user.name')); + var userLastInput = element(by.model('user.last')); + + it('should initialize to model', function() { + expect(user.getText()).toContain('{"name":"guest","last":"visitor"}'); + expect(userNameValid.getText()).toContain('true'); + expect(formValid.getText()).toContain('true'); + }); + + it('should be invalid if empty when required', function() { + userNameInput.clear(); + userNameInput.sendKeys(''); + + expect(user.getText()).toContain('{"last":"visitor"}'); + expect(userNameValid.getText()).toContain('false'); + expect(formValid.getText()).toContain('false'); + }); + + it('should be valid if empty when min length is set', function() { + userLastInput.clear(); + userLastInput.sendKeys(''); + + expect(user.getText()).toContain('{"name":"guest","last":""}'); + expect(lastNameValid.getText()).toContain('true'); + expect(formValid.getText()).toContain('true'); + }); + + it('should be invalid if less than required min length', function() { + userLastInput.clear(); + userLastInput.sendKeys('xx'); + + expect(user.getText()).toContain('{"name":"guest"}'); + expect(lastNameValid.getText()).toContain('false'); + expect(lastNameError.getText()).toContain('minlength'); + expect(formValid.getText()).toContain('false'); + }); + + it('should be invalid if longer than max length', function() { + userLastInput.clear(); + userLastInput.sendKeys('some ridiculously long name'); + + expect(user.getText()).toContain('{"name":"guest"}'); + expect(lastNameValid.getText()).toContain('false'); + expect(lastNameError.getText()).toContain('maxlength'); + expect(formValid.getText()).toContain('false'); + }); + +
+ */ +var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) { + return { + restrict: 'E', + require: '?ngModel', + link: function(scope, element, attr, ctrl) { + if (ctrl) { + (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer, + $browser); + } + } + }; +}]; + +var VALID_CLASS = 'ng-valid', + INVALID_CLASS = 'ng-invalid', + PRISTINE_CLASS = 'ng-pristine', + DIRTY_CLASS = 'ng-dirty'; + +/** + * @ngdoc type + * @name ngModel.NgModelController + * + * @property {string} $viewValue Actual string value in the view. + * @property {*} $modelValue The value in the model, that the control is bound to. + * @property {Array.} $parsers Array of functions to execute, as a pipeline, whenever + the control reads value from the DOM. Each function is called, in turn, passing the value + through to the next. The last return value is used to populate the model. + Used to sanitize / convert the value as well as validation. For validation, + the parsers should update the validity state using + {@link ngModel.NgModelController#$setValidity $setValidity()}, + and return `undefined` for invalid values. + + * + * @property {Array.} $formatters Array of functions to execute, as a pipeline, whenever + the model value changes. Each function is called, in turn, passing the value through to the + next. Used to format / convert values for display in the control and validation. + * ```js + * function formatter(value) { + * if (value) { + * return value.toUpperCase(); + * } + * } + * ngModel.$formatters.push(formatter); + * ``` + * + * @property {Array.} $viewChangeListeners Array of functions to execute whenever the + * view value has changed. It is called with no arguments, and its return value is ignored. + * This can be used in place of additional $watches against the model value. + * + * @property {Object} $error An object hash with all errors as keys. + * + * @property {boolean} $pristine True if user has not interacted with the control yet. + * @property {boolean} $dirty True if user has already interacted with the control. + * @property {boolean} $valid True if there is no error. + * @property {boolean} $invalid True if at least one error on the control. + * + * @description + * + * `NgModelController` provides API for the `ng-model` directive. The controller contains + * services for data-binding, validation, CSS updates, and value formatting and parsing. It + * purposefully does not contain any logic which deals with DOM rendering or listening to + * DOM events. Such DOM related logic should be provided by other directives which make use of + * `NgModelController` for data-binding. + * + * ## Custom Control Example + * This example shows how to use `NgModelController` with a custom control to achieve + * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) + * collaborate together to achieve the desired result. + * + * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element + * contents be edited in place by the user. This will not work on older browsers. + * + * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} + * module to automatically remove "bad" content like inline event listener (e.g. ``). + * However, as we are using `$sce` the model can still decide to to provide unsafe content if it marks + * that content using the `$sce` service. + * + * + + [contenteditable] { + border: 1px solid black; + background-color: white; + min-height: 20px; + } + + .ng-invalid { + border: 1px solid red; + } + + + + angular.module('customControl', ['ngSanitize']). + directive('contenteditable', ['$sce', function($sce) { + return { + restrict: 'A', // only activate on element attribute + require: '?ngModel', // get a hold of NgModelController + link: function(scope, element, attrs, ngModel) { + if(!ngModel) return; // do nothing if no ng-model + + // Specify how UI should be updated + ngModel.$render = function() { + element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); + }; + + // Listen for change events to enable binding + element.on('blur keyup change', function() { + scope.$apply(read); + }); + read(); // initialize + + // Write data to the model + function read() { + var html = element.html(); + // When we clear the content editable the browser leaves a
behind + // If strip-br attribute is provided then we strip this out + if( attrs.stripBr && html == '
' ) { + html = ''; + } + ngModel.$setViewValue(html); + } + } + }; + }]); +
+ +
+
Change me!
+ Required! +
+ +
+
+ + it('should data-bind and become invalid', function() { + if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') { + // SafariDriver can't handle contenteditable + // and Firefox driver can't clear contenteditables very well + return; + } + var contentEditable = element(by.css('[contenteditable]')); + var content = 'Change me!'; + + expect(contentEditable.getText()).toEqual(content); + + contentEditable.clear(); + contentEditable.sendKeys(protractor.Key.BACK_SPACE); + expect(contentEditable.getText()).toEqual(''); + expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/); + }); + + *
+ * + * + */ +var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', + function($scope, $exceptionHandler, $attr, $element, $parse, $animate) { + this.$viewValue = Number.NaN; + this.$modelValue = Number.NaN; + this.$parsers = []; + this.$formatters = []; + this.$viewChangeListeners = []; + this.$pristine = true; + this.$dirty = false; + this.$valid = true; + this.$invalid = false; + this.$name = $attr.name; + + var ngModelGet = $parse($attr.ngModel), + ngModelSet = ngModelGet.assign; + + if (!ngModelSet) { + throw minErr('ngModel')('nonassign', "Expression '{0}' is non-assignable. Element: {1}", + $attr.ngModel, startingTag($element)); + } + + /** + * @ngdoc method + * @name ngModel.NgModelController#$render + * + * @description + * Called when the view needs to be updated. It is expected that the user of the ng-model + * directive will implement this method. + */ + this.$render = noop; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$isEmpty + * + * @description + * This is called when we need to determine if the value of the input is empty. + * + * For instance, the required directive does this to work out if the input has data or not. + * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. + * + * You can override this for input directives whose concept of being empty is different to the + * default. The `checkboxInputType` directive does this because in its case a value of `false` + * implies empty. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is empty. + */ + this.$isEmpty = function(value) { + return isUndefined(value) || value === '' || value === null || value !== value; + }; + + var parentForm = $element.inheritedData('$formController') || nullFormCtrl, + invalidCount = 0, // used to easily determine if we are valid + $error = this.$error = {}; // keep invalid keys here + + + // Setup initial state of the control + $element.addClass(PRISTINE_CLASS); + toggleValidCss(true); + + // convenience method for easy toggling of classes + function toggleValidCss(isValid, validationErrorKey) { + validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; + $animate.removeClass($element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey); + $animate.addClass($element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); + } + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setValidity + * + * @description + * Change the validity state, and notifies the form when the control changes validity. (i.e. it + * does not notify form if given validator is already marked as invalid). + * + * This method should be called by validators - i.e. the parser or formatter functions. + * + * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign + * to `$error[validationErrorKey]=!isValid` so that it is available for data-binding. + * The `validationErrorKey` should be in camelCase and will get converted into dash-case + * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` + * class and can be bound to as `{{someForm.someControl.$error.myError}}` . + * @param {boolean} isValid Whether the current state is valid (true) or invalid (false). + */ + this.$setValidity = function(validationErrorKey, isValid) { + // Purposeful use of ! here to cast isValid to boolean in case it is undefined + // jshint -W018 + if ($error[validationErrorKey] === !isValid) return; + // jshint +W018 + + if (isValid) { + if ($error[validationErrorKey]) invalidCount--; + if (!invalidCount) { + toggleValidCss(true); + this.$valid = true; + this.$invalid = false; + } + } else { + toggleValidCss(false); + this.$invalid = true; + this.$valid = false; + invalidCount++; + } + + $error[validationErrorKey] = !isValid; + toggleValidCss(isValid, validationErrorKey); + + parentForm.$setValidity(validationErrorKey, isValid, this); + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setPristine + * + * @description + * Sets the control to its pristine state. + * + * This method can be called to remove the 'ng-dirty' class and set the control to its pristine + * state (ng-pristine class). + */ + this.$setPristine = function () { + this.$dirty = false; + this.$pristine = true; + $animate.removeClass($element, DIRTY_CLASS); + $animate.addClass($element, PRISTINE_CLASS); + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setViewValue + * + * @description + * Update the view value. + * + * This method should be called when the view value changes, typically from within a DOM event handler. + * For example {@link ng.directive:input input} and + * {@link ng.directive:select select} directives call it. + * + * It will update the $viewValue, then pass this value through each of the functions in `$parsers`, + * which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to + * `$modelValue` and the **expression** specified in the `ng-model` attribute. + * + * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called. + * + * Note that calling this function does not trigger a `$digest`. + * + * @param {string} value Value from the view. + */ + this.$setViewValue = function(value) { + this.$viewValue = value; + + // change to dirty + if (this.$pristine) { + this.$dirty = true; + this.$pristine = false; + $animate.removeClass($element, PRISTINE_CLASS); + $animate.addClass($element, DIRTY_CLASS); + parentForm.$setDirty(); + } + + forEach(this.$parsers, function(fn) { + value = fn(value); + }); + + if (this.$modelValue !== value) { + this.$modelValue = value; + ngModelSet($scope, value); + forEach(this.$viewChangeListeners, function(listener) { + try { + listener(); + } catch(e) { + $exceptionHandler(e); + } + }); + } + }; + + // model -> value + var ctrl = this; + + $scope.$watch(function ngModelWatch() { + var value = ngModelGet($scope); + + // if scope model value and ngModel value are out of sync + if (ctrl.$modelValue !== value) { + + var formatters = ctrl.$formatters, + idx = formatters.length; + + ctrl.$modelValue = value; + while(idx--) { + value = formatters[idx](value); + } + + if (ctrl.$viewValue !== value) { + ctrl.$viewValue = value; + ctrl.$render(); + } + } + + return value; + }); +}]; + + +/** + * @ngdoc directive + * @name ngModel + * + * @element input + * + * @description + * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a + * property on the scope using {@link ngModel.NgModelController NgModelController}, + * which is created and exposed by this directive. + * + * `ngModel` is responsible for: + * + * - Binding the view into the model, which other directives such as `input`, `textarea` or `select` + * require. + * - Providing validation behavior (i.e. required, number, email, url). + * - Keeping the state of the control (valid/invalid, dirty/pristine, validation errors). + * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`) including animations. + * - Registering the control with its parent {@link ng.directive:form form}. + * + * Note: `ngModel` will try to bind to the property given by evaluating the expression on the + * current scope. If the property doesn't already exist on this scope, it will be created + * implicitly and added to the scope. + * + * For best practices on using `ngModel`, see: + * + * - [https://github.com/angular/angular.js/wiki/Understanding-Scopes] + * + * For basic examples, how to use `ngModel`, see: + * + * - {@link ng.directive:input input} + * - {@link input[text] text} + * - {@link input[checkbox] checkbox} + * - {@link input[radio] radio} + * - {@link input[number] number} + * - {@link input[email] email} + * - {@link input[url] url} + * - {@link ng.directive:select select} + * - {@link ng.directive:textarea textarea} + * + * # CSS classes + * The following CSS classes are added and removed on the associated input/select/textarea element + * depending on the validity of the model. + * + * - `ng-valid` is set if the model is valid. + * - `ng-invalid` is set if the model is invalid. + * - `ng-pristine` is set if the model is pristine. + * - `ng-dirty` is set if the model is dirty. + * + * Keep in mind that ngAnimate can detect each of these classes when added and removed. + * + * ## Animation Hooks + * + * Animations within models are triggered when any of the associated CSS classes are added and removed + * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`, + * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself. + * The animations that are triggered within ngModel are similar to how they work in ngClass and + * animations can be hooked into using CSS transitions, keyframes as well as JS animations. + * + * The following example shows a simple way to utilize CSS transitions to style an input element + * that has been rendered as invalid after it has been validated: + * + *
+ * //be sure to include ngAnimate as a module to hook into more
+ * //advanced animations
+ * .my-input {
+ *   transition:0.5s linear all;
+ *   background: white;
+ * }
+ * .my-input.ng-invalid {
+ *   background: red;
+ *   color:white;
+ * }
+ * 
+ * + * @example + * + + + + Update input to see transitions when valid/invalid. + Integer is a valid value. +
+ +
+
+ *
+ */ +var ngModelDirective = function() { + return { + require: ['ngModel', '^?form'], + controller: NgModelController, + link: function(scope, element, attr, ctrls) { + // notify others, especially parent forms + + var modelCtrl = ctrls[0], + formCtrl = ctrls[1] || nullFormCtrl; + + formCtrl.$addControl(modelCtrl); + + scope.$on('$destroy', function() { + formCtrl.$removeControl(modelCtrl); + }); + } + }; +}; + + +/** + * @ngdoc directive + * @name ngChange + * + * @description + * Evaluate the given expression when the user changes the input. + * The expression is evaluated immediately, unlike the JavaScript onchange event + * which only triggers at the end of a change (usually, when the user leaves the + * form element or presses the return key). + * The expression is not evaluated when the value change is coming from the model. + * + * Note, this directive requires `ngModel` to be present. + * + * @element input + * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change + * in input value. + * + * @example + * + * + * + *
+ * + * + *
+ * debug = {{confirmed}}
+ * counter = {{counter}}
+ *
+ *
+ * + * var counter = element(by.binding('counter')); + * var debug = element(by.binding('confirmed')); + * + * it('should evaluate the expression if changing from view', function() { + * expect(counter.getText()).toContain('0'); + * + * element(by.id('ng-change-example1')).click(); + * + * expect(counter.getText()).toContain('1'); + * expect(debug.getText()).toContain('true'); + * }); + * + * it('should not evaluate the expression if changing from model', function() { + * element(by.id('ng-change-example2')).click(); + + * expect(counter.getText()).toContain('0'); + * expect(debug.getText()).toContain('true'); + * }); + * + *
+ */ +var ngChangeDirective = valueFn({ + require: 'ngModel', + link: function(scope, element, attr, ctrl) { + ctrl.$viewChangeListeners.push(function() { + scope.$eval(attr.ngChange); + }); + } +}); + + +var requiredDirective = function() { + return { + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + attr.required = true; // force truthy in case we are on non input element + + var validator = function(value) { + if (attr.required && ctrl.$isEmpty(value)) { + ctrl.$setValidity('required', false); + return; + } else { + ctrl.$setValidity('required', true); + return value; + } + }; + + ctrl.$formatters.push(validator); + ctrl.$parsers.unshift(validator); + + attr.$observe('required', function() { + validator(ctrl.$viewValue); + }); + } + }; +}; + + +/** + * @ngdoc directive + * @name ngList + * + * @description + * Text input that converts between a delimited string and an array of strings. The delimiter + * can be a fixed string (by default a comma) or a regular expression. + * + * @element input + * @param {string=} ngList optional delimiter that should be used to split the value. If + * specified in form `/something/` then the value will be converted into a regular expression. + * + * @example + + + +
+ List: + + Required! +
+ names = {{names}}
+ myForm.namesInput.$valid = {{myForm.namesInput.$valid}}
+ myForm.namesInput.$error = {{myForm.namesInput.$error}}
+ myForm.$valid = {{myForm.$valid}}
+ myForm.$error.required = {{!!myForm.$error.required}}
+
+
+ + var listInput = element(by.model('names')); + var names = element(by.binding('{{names}}')); + var valid = element(by.binding('myForm.namesInput.$valid')); + var error = element(by.css('span.error')); + + it('should initialize to model', function() { + expect(names.getText()).toContain('["igor","misko","vojta"]'); + expect(valid.getText()).toContain('true'); + expect(error.getCssValue('display')).toBe('none'); + }); + + it('should be invalid if empty', function() { + listInput.clear(); + listInput.sendKeys(''); + + expect(names.getText()).toContain(''); + expect(valid.getText()).toContain('false'); + expect(error.getCssValue('display')).not.toBe('none'); }); + +
+ */ +var ngListDirective = function() { + return { + require: 'ngModel', + link: function(scope, element, attr, ctrl) { + var match = /\/(.*)\//.exec(attr.ngList), + separator = match && new RegExp(match[1]) || attr.ngList || ','; + + var parse = function(viewValue) { + // If the viewValue is invalid (say required but empty) it will be `undefined` + if (isUndefined(viewValue)) return; + + var list = []; + + if (viewValue) { + forEach(viewValue.split(separator), function(value) { + if (value) list.push(trim(value)); + }); + } + + return list; + }; + + ctrl.$parsers.push(parse); + ctrl.$formatters.push(function(value) { + if (isArray(value)) { + return value.join(', '); + } + + return undefined; + }); + + // Override the standard $isEmpty because an empty array means the input is empty. + ctrl.$isEmpty = function(value) { + return !value || !value.length; + }; + } + }; +}; + + +var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; +/** + * @ngdoc directive + * @name ngValue + * + * @description + * Binds the given expression to the value of `input[select]` or `input[radio]`, so + * that when the element is selected, the `ngModel` of that element is set to the + * bound value. + * + * `ngValue` is useful when dynamically generating lists of radio buttons using `ng-repeat`, as + * shown below. + * + * @element input + * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute + * of the `input` element + * + * @example + + + +
+

Which is your favorite?

+ +
You chose {{my.favorite}}
+
+
+ + var favorite = element(by.binding('my.favorite')); + + it('should initialize to model', function() { + expect(favorite.getText()).toContain('unicorns'); + }); + it('should bind the values to the inputs', function() { + element.all(by.model('my.favorite')).get(0).click(); + expect(favorite.getText()).toContain('pizza'); + }); + +
+ */ +var ngValueDirective = function() { + return { + priority: 100, + compile: function(tpl, tplAttr) { + if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { + return function ngValueConstantLink(scope, elm, attr) { + attr.$set('value', scope.$eval(attr.ngValue)); + }; + } else { + return function ngValueLink(scope, elm, attr) { + scope.$watch(attr.ngValue, function valueWatchAction(value) { + attr.$set('value', value); + }); + }; + } + } + }; +}; + +/** + * @ngdoc directive + * @name ngBind + * @restrict AC + * + * @description + * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element + * with the value of a given expression, and to update the text content when the value of that + * expression changes. + * + * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like + * `{{ expression }}` which is similar but less verbose. + * + * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily + * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an + * element attribute, it makes the bindings invisible to the user while the page is loading. + * + * An alternative solution to this problem would be using the + * {@link ng.directive:ngCloak ngCloak} directive. + * + * + * @element ANY + * @param {expression} ngBind {@link guide/expression Expression} to evaluate. + * + * @example + * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. + + + +
+ Enter name:
+ Hello ! +
+
+ + it('should check ng-bind', function() { + var nameInput = element(by.model('name')); + + expect(element(by.binding('name')).getText()).toBe('Whirled'); + nameInput.clear(); + nameInput.sendKeys('world'); + expect(element(by.binding('name')).getText()).toBe('world'); + }); + +
+ */ +var ngBindDirective = ngDirective({ + compile: function(templateElement) { + templateElement.addClass('ng-binding'); + return function (scope, element, attr) { + element.data('$binding', attr.ngBind); + scope.$watch(attr.ngBind, function ngBindWatchAction(value) { + // We are purposefully using == here rather than === because we want to + // catch when value is "null or undefined" + // jshint -W041 + element.text(value == undefined ? '' : value); + }); + }; + } +}); + + +/** + * @ngdoc directive + * @name ngBindTemplate + * + * @description + * The `ngBindTemplate` directive specifies that the element + * text content should be replaced with the interpolation of the template + * in the `ngBindTemplate` attribute. + * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}` + * expressions. This directive is needed since some HTML elements + * (such as TITLE and OPTION) cannot contain SPAN elements. + * + * @element ANY + * @param {string} ngBindTemplate template of form + * {{ expression }} to eval. + * + * @example + * Try it here: enter text in text box and watch the greeting change. + + + +
+ Salutation:
+ Name:
+

+       
+
+ + it('should check ng-bind', function() { + var salutationElem = element(by.binding('salutation')); + var salutationInput = element(by.model('salutation')); + var nameInput = element(by.model('name')); + + expect(salutationElem.getText()).toBe('Hello World!'); + + salutationInput.clear(); + salutationInput.sendKeys('Greetings'); + nameInput.clear(); + nameInput.sendKeys('user'); + + expect(salutationElem.getText()).toBe('Greetings user!'); + }); + +
+ */ +var ngBindTemplateDirective = ['$interpolate', function($interpolate) { + return function(scope, element, attr) { + // TODO: move this to scenario runner + var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); + element.addClass('ng-binding').data('$binding', interpolateFn); + attr.$observe('ngBindTemplate', function(value) { + element.text(value); + }); + }; +}]; + + +/** + * @ngdoc directive + * @name ngBindHtml + * + * @description + * Creates a binding that will innerHTML the result of evaluating the `expression` into the current + * element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link + * ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize` + * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in + * core Angular.) You may also bypass sanitization for values you know are safe. To do so, bind to + * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example + * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}. + * + * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you + * will have an exception (instead of an exploit.) + * + * @element ANY + * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. + * + * @example + + + +
+

+
+
+ + + angular.module('bindHtmlExample', ['ngSanitize']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.myHTML = + 'I am an HTMLstring with ' + + 'links! and other stuff'; + }]); + + + + it('should check ng-bind-html', function() { + expect(element(by.binding('myHTML')).getText()).toBe( + 'I am an HTMLstring with links! and other stuff'); + }); + +
+ */ +var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) { + return { + compile: function (tElement) { + tElement.addClass('ng-binding'); + + return function (scope, element, attr) { + element.data('$binding', attr.ngBindHtml); + + var parsed = $parse(attr.ngBindHtml); + + function getStringValue() { + return (parsed(scope) || '').toString(); + } + + scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) { + element.html($sce.getTrustedHtml(parsed(scope)) || ''); + }); + }; + } + }; +}]; + +function classDirective(name, selector) { + name = 'ngClass' + name; + return ['$animate', function($animate) { + return { + restrict: 'AC', + link: function(scope, element, attr) { + var oldVal; + + scope.$watch(attr[name], ngClassWatchAction, true); + + attr.$observe('class', function(value) { + ngClassWatchAction(scope.$eval(attr[name])); + }); + + + if (name !== 'ngClass') { + scope.$watch('$index', function($index, old$index) { + // jshint bitwise: false + var mod = $index & 1; + if (mod !== (old$index & 1)) { + var classes = arrayClasses(scope.$eval(attr[name])); + mod === selector ? + addClasses(classes) : + removeClasses(classes); + } + }); + } + + function addClasses(classes) { + var newClasses = digestClassCounts(classes, 1); + attr.$addClass(newClasses); + } + + function removeClasses(classes) { + var newClasses = digestClassCounts(classes, -1); + attr.$removeClass(newClasses); + } + + function digestClassCounts (classes, count) { + var classCounts = element.data('$classCounts') || {}; + var classesToUpdate = []; + forEach(classes, function (className) { + if (count > 0 || classCounts[className]) { + classCounts[className] = (classCounts[className] || 0) + count; + if (classCounts[className] === +(count > 0)) { + classesToUpdate.push(className); + } + } + }); + element.data('$classCounts', classCounts); + return classesToUpdate.join(' '); + } + + function updateClasses (oldClasses, newClasses) { + var toAdd = arrayDifference(newClasses, oldClasses); + var toRemove = arrayDifference(oldClasses, newClasses); + toRemove = digestClassCounts(toRemove, -1); + toAdd = digestClassCounts(toAdd, 1); + + if (toAdd.length === 0) { + $animate.removeClass(element, toRemove); + } else if (toRemove.length === 0) { + $animate.addClass(element, toAdd); + } else { + $animate.setClass(element, toAdd, toRemove); + } + } + + function ngClassWatchAction(newVal) { + if (selector === true || scope.$index % 2 === selector) { + var newClasses = arrayClasses(newVal || []); + if (!oldVal) { + addClasses(newClasses); + } else if (!equals(newVal,oldVal)) { + var oldClasses = arrayClasses(oldVal); + updateClasses(oldClasses, newClasses); + } + } + oldVal = shallowCopy(newVal); + } + } + }; + + function arrayDifference(tokens1, tokens2) { + var values = []; + + outer: + for(var i = 0; i < tokens1.length; i++) { + var token = tokens1[i]; + for(var j = 0; j < tokens2.length; j++) { + if(token == tokens2[j]) continue outer; + } + values.push(token); + } + return values; + } + + function arrayClasses (classVal) { + if (isArray(classVal)) { + return classVal; + } else if (isString(classVal)) { + return classVal.split(' '); + } else if (isObject(classVal)) { + var classes = [], i = 0; + forEach(classVal, function(v, k) { + if (v) { + classes = classes.concat(k.split(' ')); + } + }); + return classes; + } + return classVal; + } + }]; +} + +/** + * @ngdoc directive + * @name ngClass + * @restrict AC + * + * @description + * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding + * an expression that represents all classes to be added. + * + * The directive operates in three different ways, depending on which of three types the expression + * evaluates to: + * + * 1. If the expression evaluates to a string, the string should be one or more space-delimited class + * names. + * + * 2. If the expression evaluates to an array, each element of the array should be a string that is + * one or more space-delimited class names. + * + * 3. If the expression evaluates to an object, then for each key-value pair of the + * object with a truthy value the corresponding key is used as a class name. + * + * The directive won't add duplicate classes if a particular class was already set. + * + * When the expression changes, the previously added classes are removed and only then the + * new classes are added. + * + * @animations + * add - happens just before the class is applied to the element + * remove - happens just before the class is removed from the element + * + * @element ANY + * @param {expression} ngClass {@link guide/expression Expression} to eval. The result + * of the evaluation can be a string representing space delimited class + * names, an array, or a map of class names to boolean values. In the case of a map, the + * names of the properties whose values are truthy will be added as css classes to the + * element. + * + * @example Example that demonstrates basic bindings via ngClass directive. + + +

Map Syntax Example

+ deleted (apply "strike" class)
+ important (apply "bold" class)
+ error (apply "red" class) +
+

Using String Syntax

+ +
+

Using Array Syntax

+
+
+
+
+ + .strike { + text-decoration: line-through; + } + .bold { + font-weight: bold; + } + .red { + color: red; + } + + + var ps = element.all(by.css('p')); + + it('should let you toggle the class', function() { + + expect(ps.first().getAttribute('class')).not.toMatch(/bold/); + expect(ps.first().getAttribute('class')).not.toMatch(/red/); + + element(by.model('important')).click(); + expect(ps.first().getAttribute('class')).toMatch(/bold/); + + element(by.model('error')).click(); + expect(ps.first().getAttribute('class')).toMatch(/red/); + }); + + it('should let you toggle string example', function() { + expect(ps.get(1).getAttribute('class')).toBe(''); + element(by.model('style')).clear(); + element(by.model('style')).sendKeys('red'); + expect(ps.get(1).getAttribute('class')).toBe('red'); + }); + + it('array example should have 3 classes', function() { + expect(ps.last().getAttribute('class')).toBe(''); + element(by.model('style1')).sendKeys('bold'); + element(by.model('style2')).sendKeys('strike'); + element(by.model('style3')).sendKeys('red'); + expect(ps.last().getAttribute('class')).toBe('bold strike red'); + }); + +
+ + ## Animations + + The example below demonstrates how to perform animations using ngClass. + + + + + +
+ Sample Text +
+ + .base-class { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + } + + .base-class.my-class { + color: red; + font-size:3em; + } + + + it('should check ng-class', function() { + expect(element(by.css('.base-class')).getAttribute('class')).not. + toMatch(/my-class/); + + element(by.id('setbtn')).click(); + + expect(element(by.css('.base-class')).getAttribute('class')). + toMatch(/my-class/); + + element(by.id('clearbtn')).click(); + + expect(element(by.css('.base-class')).getAttribute('class')).not. + toMatch(/my-class/); + }); + +
+ + + ## ngClass and pre-existing CSS3 Transitions/Animations + The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure. + Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder + any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure + to view the step by step details of {@link ngAnimate.$animate#addclass $animate.addClass} and + {@link ngAnimate.$animate#removeclass $animate.removeClass}. + */ +var ngClassDirective = classDirective('', true); + +/** + * @ngdoc directive + * @name ngClassOdd + * @restrict AC + * + * @description + * The `ngClassOdd` and `ngClassEven` directives work exactly as + * {@link ng.directive:ngClass ngClass}, except they work in + * conjunction with `ngRepeat` and take effect only on odd (even) rows. + * + * This directive can be applied only within the scope of an + * {@link ng.directive:ngRepeat ngRepeat}. + * + * @element ANY + * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result + * of the evaluation can be a string representing space delimited class names or an array. + * + * @example + + +
    +
  1. + + {{name}} + +
  2. +
+
+ + .odd { + color: red; + } + .even { + color: blue; + } + + + it('should check ng-class-odd and ng-class-even', function() { + expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). + toMatch(/odd/); + expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). + toMatch(/even/); + }); + +
+ */ +var ngClassOddDirective = classDirective('Odd', 0); + +/** + * @ngdoc directive + * @name ngClassEven + * @restrict AC + * + * @description + * The `ngClassOdd` and `ngClassEven` directives work exactly as + * {@link ng.directive:ngClass ngClass}, except they work in + * conjunction with `ngRepeat` and take effect only on odd (even) rows. + * + * This directive can be applied only within the scope of an + * {@link ng.directive:ngRepeat ngRepeat}. + * + * @element ANY + * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The + * result of the evaluation can be a string representing space delimited class names or an array. + * + * @example + + +
    +
  1. + + {{name}}       + +
  2. +
+
+ + .odd { + color: red; + } + .even { + color: blue; + } + + + it('should check ng-class-odd and ng-class-even', function() { + expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). + toMatch(/odd/); + expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). + toMatch(/even/); + }); + +
+ */ +var ngClassEvenDirective = classDirective('Even', 1); + +/** + * @ngdoc directive + * @name ngCloak + * @restrict AC + * + * @description + * The `ngCloak` directive is used to prevent the Angular html template from being briefly + * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this + * directive to avoid the undesirable flicker effect caused by the html template display. + * + * The directive can be applied to the `` element, but the preferred usage is to apply + * multiple `ngCloak` directives to small portions of the page to permit progressive rendering + * of the browser view. + * + * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and + * `angular.min.js`. + * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). + * + * ```css + * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { + * display: none !important; + * } + * ``` + * + * When this css rule is loaded by the browser, all html elements (including their children) that + * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive + * during the compilation of the template it deletes the `ngCloak` element attribute, making + * the compiled element visible. + * + * For the best result, the `angular.js` script must be loaded in the head section of the html + * document; alternatively, the css rule above must be included in the external stylesheet of the + * application. + * + * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they + * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css + * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below. + * + * @element ANY + * + * @example + + +
{{ 'hello' }}
+
{{ 'hello IE7' }}
+
+ + it('should remove the template directive and css class', function() { + expect($('#template1').getAttribute('ng-cloak')). + toBeNull(); + expect($('#template2').getAttribute('ng-cloak')). + toBeNull(); + }); + +
+ * + */ +var ngCloakDirective = ngDirective({ + compile: function(element, attr) { + attr.$set('ngCloak', undefined); + element.removeClass('ng-cloak'); + } +}); + +/** + * @ngdoc directive + * @name ngController + * + * @description + * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular + * supports the principles behind the Model-View-Controller design pattern. + * + * MVC components in angular: + * + * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties + * are accessed through bindings. + * * View — The template (HTML with data bindings) that is rendered into the View. + * * Controller — The `ngController` directive specifies a Controller class; the class contains business + * logic behind the application to decorate the scope with functions and values + * + * Note that you can also attach controllers to the DOM by declaring it in a route definition + * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller + * again using `ng-controller` in the template itself. This will cause the controller to be attached + * and executed twice. + * + * @element ANY + * @scope + * @param {expression} ngController Name of a globally accessible constructor function or an + * {@link guide/expression expression} that on the current scope evaluates to a + * constructor function. The controller instance can be published into a scope property + * by specifying `as propertyName`. + * + * @example + * Here is a simple form for editing user contact information. Adding, removing, clearing, and + * greeting are methods declared on the controller (see source tab). These methods can + * easily be called from the angular markup. Any changes to the data are automatically reflected + * in the View without the need for a manual update. + * + * Two different declaration styles are included below: + * + * * one binds methods and properties directly onto the controller using `this`: + * `ng-controller="SettingsController1 as settings"` + * * one injects `$scope` into the controller: + * `ng-controller="SettingsController2"` + * + * The second option is more common in the Angular community, and is generally used in boilerplates + * and in this guide. However, there are advantages to binding properties directly to the controller + * and avoiding scope. + * + * * Using `controller as` makes it obvious which controller you are accessing in the template when + * multiple controllers apply to an element. + * * If you are writing your controllers as classes you have easier access to the properties and + * methods, which will appear on the scope, from inside the controller code. + * * Since there is always a `.` in the bindings, you don't have to worry about prototypal + * inheritance masking primitives. + * + * This example demonstrates the `controller as` syntax. + * + * + * + *
+ * Name: + * [ greet ]
+ * Contact: + *
    + *
  • + * + * + * [ clear + * | X ] + *
  • + *
  • [ add ]
  • + *
+ *
+ *
+ * + * angular.module('controllerAsExample', []) + * .controller('SettingsController1', SettingsController1); + * + * function SettingsController1() { + * this.name = "John Smith"; + * this.contacts = [ + * {type: 'phone', value: '408 555 1212'}, + * {type: 'email', value: 'john.smith@example.org'} ]; + * } + * + * SettingsController1.prototype.greet = function() { + * alert(this.name); + * }; + * + * SettingsController1.prototype.addContact = function() { + * this.contacts.push({type: 'email', value: 'yourname@example.org'}); + * }; + * + * SettingsController1.prototype.removeContact = function(contactToRemove) { + * var index = this.contacts.indexOf(contactToRemove); + * this.contacts.splice(index, 1); + * }; + * + * SettingsController1.prototype.clearContact = function(contact) { + * contact.type = 'phone'; + * contact.value = ''; + * }; + * + * + * it('should check controller as', function() { + * var container = element(by.id('ctrl-as-exmpl')); + * expect(container.element(by.model('settings.name')) + * .getAttribute('value')).toBe('John Smith'); + * + * var firstRepeat = + * container.element(by.repeater('contact in settings.contacts').row(0)); + * var secondRepeat = + * container.element(by.repeater('contact in settings.contacts').row(1)); + * + * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe('408 555 1212'); + * + * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe('john.smith@example.org'); + * + * firstRepeat.element(by.linkText('clear')).click(); + * + * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe(''); + * + * container.element(by.linkText('add')).click(); + * + * expect(container.element(by.repeater('contact in settings.contacts').row(2)) + * .element(by.model('contact.value')) + * .getAttribute('value')) + * .toBe('yourname@example.org'); + * }); + * + *
+ * + * This example demonstrates the "attach to `$scope`" style of controller. + * + * + * + *
+ * Name: + * [ greet ]
+ * Contact: + *
    + *
  • + * + * + * [ clear + * | X ] + *
  • + *
  • [ add ]
  • + *
+ *
+ *
+ * + * angular.module('controllerExample', []) + * .controller('SettingsController2', ['$scope', SettingsController2]); + * + * function SettingsController2($scope) { + * $scope.name = "John Smith"; + * $scope.contacts = [ + * {type:'phone', value:'408 555 1212'}, + * {type:'email', value:'john.smith@example.org'} ]; + * + * $scope.greet = function() { + * alert($scope.name); + * }; + * + * $scope.addContact = function() { + * $scope.contacts.push({type:'email', value:'yourname@example.org'}); + * }; + * + * $scope.removeContact = function(contactToRemove) { + * var index = $scope.contacts.indexOf(contactToRemove); + * $scope.contacts.splice(index, 1); + * }; + * + * $scope.clearContact = function(contact) { + * contact.type = 'phone'; + * contact.value = ''; + * }; + * } + * + * + * it('should check controller', function() { + * var container = element(by.id('ctrl-exmpl')); + * + * expect(container.element(by.model('name')) + * .getAttribute('value')).toBe('John Smith'); + * + * var firstRepeat = + * container.element(by.repeater('contact in contacts').row(0)); + * var secondRepeat = + * container.element(by.repeater('contact in contacts').row(1)); + * + * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe('408 555 1212'); + * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe('john.smith@example.org'); + * + * firstRepeat.element(by.linkText('clear')).click(); + * + * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe(''); + * + * container.element(by.linkText('add')).click(); + * + * expect(container.element(by.repeater('contact in contacts').row(2)) + * .element(by.model('contact.value')) + * .getAttribute('value')) + * .toBe('yourname@example.org'); + * }); + * + *
+ + */ +var ngControllerDirective = [function() { + return { + scope: true, + controller: '@', + priority: 500 + }; +}]; + +/** + * @ngdoc directive + * @name ngCsp + * + * @element html + * @description + * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. + * + * This is necessary when developing things like Google Chrome Extensions. + * + * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). + * For Angular to be CSP compatible there are only two things that we need to do differently: + * + * - don't use `Function` constructor to generate optimized value getters + * - don't inject custom stylesheet into the document + * + * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp` + * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will + * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will + * be raised. + * + * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically + * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}). + * To make those directives work in CSP mode, include the `angular-csp.css` manually. + * + * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This + * autodetection however triggers a CSP error to be logged in the console: + * + * ``` + * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of + * script in the following Content Security Policy directive: "default-src 'self'". Note that + * 'script-src' was not explicitly set, so 'default-src' is used as a fallback. + * ``` + * + * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp` + * directive on the root element of the application or on the `angular.js` script tag, whichever + * appears first in the html document. + * + * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.* + * + * @example + * This example shows how to apply the `ngCsp` directive to the `html` tag. + ```html + + + ... + ... + + ``` + */ + +// ngCsp is not implemented as a proper directive any more, because we need it be processed while we +// bootstrap the system (before $parse is instantiated), for this reason we just have +// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc + +/** + * @ngdoc directive + * @name ngClick + * + * @description + * The ngClick directive allows you to specify custom behavior when + * an element is clicked. + * + * @element ANY + * @priority 0 + * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon + * click. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + + count: {{count}} + + + + it('should check ng-click', function() { + expect(element(by.binding('count')).getText()).toMatch('0'); + element(by.css('button')).click(); + expect(element(by.binding('count')).getText()).toMatch('1'); + }); + + + */ +/* + * A directive that allows creation of custom onclick handlers that are defined as angular + * expressions and are compiled and executed within the current scope. + * + * Events that are handled via these handler are always configured not to propagate further. + */ +var ngEventDirectives = {}; + +// For events that might fire synchronously during DOM manipulation +// we need to execute their event handlers asynchronously using $evalAsync, +// so that they are not executed in an inconsistent state. +var forceAsyncEvents = { + 'blur': true, + 'focus': true +}; +forEach( + 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), + function(eventName) { + var directiveName = directiveNormalize('ng-' + eventName); + ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { + return { + compile: function($element, attr) { + var fn = $parse(attr[directiveName]); + return function ngEventHandler(scope, element) { + element.on(eventName, function(event) { + var callback = function() { + fn(scope, {$event:event}); + }; + if (forceAsyncEvents[eventName] && $rootScope.$$phase) { + scope.$evalAsync(callback); + } else { + scope.$apply(callback); + } + }); + }; + } + }; + }]; + } +); + +/** + * @ngdoc directive + * @name ngDblclick + * + * @description + * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event. + * + * @element ANY + * @priority 0 + * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon + * a dblclick. (The Event object is available as `$event`) + * + * @example + + + + count: {{count}} + + + */ + + +/** + * @ngdoc directive + * @name ngMousedown + * + * @description + * The ngMousedown directive allows you to specify custom behavior on mousedown event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon + * mousedown. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + count: {{count}} + + + */ + + +/** + * @ngdoc directive + * @name ngMouseup + * + * @description + * Specify custom behavior on mouseup event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon + * mouseup. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + count: {{count}} + + + */ + +/** + * @ngdoc directive + * @name ngMouseover + * + * @description + * Specify custom behavior on mouseover event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon + * mouseover. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + count: {{count}} + + + */ + + +/** + * @ngdoc directive + * @name ngMouseenter + * + * @description + * Specify custom behavior on mouseenter event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon + * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + count: {{count}} + + + */ + + +/** + * @ngdoc directive + * @name ngMouseleave + * + * @description + * Specify custom behavior on mouseleave event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon + * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + count: {{count}} + + + */ + + +/** + * @ngdoc directive + * @name ngMousemove + * + * @description + * Specify custom behavior on mousemove event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon + * mousemove. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + count: {{count}} + + + */ + + +/** + * @ngdoc directive + * @name ngKeydown + * + * @description + * Specify custom behavior on keydown event. + * + * @element ANY + * @priority 0 + * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon + * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) + * + * @example + + + + key down count: {{count}} + + + */ + + +/** + * @ngdoc directive + * @name ngKeyup + * + * @description + * Specify custom behavior on keyup event. + * + * @element ANY + * @priority 0 + * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon + * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) + * + * @example + + +

Typing in the input box below updates the key count

+ key up count: {{count}} + +

Typing in the input box below updates the keycode

+ +

event keyCode: {{ event.keyCode }}

+

event altKey: {{ event.altKey }}

+
+
+ */ + + +/** + * @ngdoc directive + * @name ngKeypress + * + * @description + * Specify custom behavior on keypress event. + * + * @element ANY + * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon + * keypress. ({@link guide/expression#-event- Event object is available as `$event`} + * and can be interrogated for keyCode, altKey, etc.) + * + * @example + + + + key press count: {{count}} + + + */ + + +/** + * @ngdoc directive + * @name ngSubmit + * + * @description + * Enables binding angular expressions to onsubmit events. + * + * Additionally it prevents the default action (which for form means sending the request to the + * server and reloading the current page), but only if the form does not contain `action`, + * `data-action`, or `x-action` attributes. + * + *
+ * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and + * `ngSubmit` handlers together. See the + * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation} + * for a detailed discussion of when `ngSubmit` may be triggered. + *
+ * + * @element form + * @priority 0 + * @param {expression} ngSubmit {@link guide/expression Expression} to eval. + * ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + +
+ Enter text and hit enter: + + +
list={{list}}
+
+
+ + it('should check ng-submit', function() { + expect(element(by.binding('list')).getText()).toBe('list=[]'); + element(by.css('#submit')).click(); + expect(element(by.binding('list')).getText()).toContain('hello'); + expect(element(by.model('text')).getAttribute('value')).toBe(''); + }); + it('should ignore empty strings', function() { + expect(element(by.binding('list')).getText()).toBe('list=[]'); + element(by.css('#submit')).click(); + element(by.css('#submit')).click(); + expect(element(by.binding('list')).getText()).toContain('hello'); + }); + +
+ */ + +/** + * @ngdoc directive + * @name ngFocus + * + * @description + * Specify custom behavior on focus event. + * + * Note: As the `focus` event is executed synchronously when calling `input.focus()` + * AngularJS executes the expression using `scope.$evalAsync` if the event is fired + * during an `$apply` to ensure a consistent state. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon + * focus. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + +/** + * @ngdoc directive + * @name ngBlur + * + * @description + * Specify custom behavior on blur event. + * + * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when + * an element has lost focus. + * + * Note: As the `blur` event is executed synchronously also during DOM manipulations + * (e.g. removing a focussed input), + * AngularJS executes the expression using `scope.$evalAsync` if the event is fired + * during an `$apply` to ensure a consistent state. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon + * blur. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + +/** + * @ngdoc directive + * @name ngCopy + * + * @description + * Specify custom behavior on copy event. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon + * copy. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + copied: {{copied}} + + + */ + +/** + * @ngdoc directive + * @name ngCut + * + * @description + * Specify custom behavior on cut event. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon + * cut. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + cut: {{cut}} + + + */ + +/** + * @ngdoc directive + * @name ngPaste + * + * @description + * Specify custom behavior on paste event. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon + * paste. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + + + + pasted: {{paste}} + + + */ + +/** + * @ngdoc directive + * @name ngIf + * @restrict A + * + * @description + * The `ngIf` directive removes or recreates a portion of the DOM tree based on an + * {expression}. If the expression assigned to `ngIf` evaluates to a false + * value then the element is removed from the DOM, otherwise a clone of the + * element is reinserted into the DOM. + * + * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the + * element in the DOM rather than changing its visibility via the `display` css property. A common + * case when this difference is significant is when using css selectors that rely on an element's + * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes. + * + * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope + * is created when the element is restored. The scope created within `ngIf` inherits from + * its parent scope using + * [prototypal inheritance](https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance). + * An important implication of this is if `ngModel` is used within `ngIf` to bind to + * a javascript primitive defined in the parent scope. In this case any modifications made to the + * variable within the child scope will override (hide) the value in the parent scope. + * + * Also, `ngIf` recreates elements using their compiled state. An example of this behavior + * is if an element's class attribute is directly modified after it's compiled, using something like + * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element + * the added class will be lost because the original compiled state is used to regenerate the element. + * + * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter` + * and `leave` effects. + * + * @animations + * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container + * leave - happens just before the ngIf contents are removed from the DOM + * + * @element ANY + * @scope + * @priority 600 + * @param {expression} ngIf If the {@link guide/expression expression} is falsy then + * the element is removed from the DOM tree. If it is truthy a copy of the compiled + * element is added to the DOM tree. + * + * @example + + + Click me:
+ Show when checked: + + I'm removed when the checkbox is unchecked. + +
+ + .animate-if { + background:white; + border:1px solid black; + padding:10px; + } + + .animate-if.ng-enter, .animate-if.ng-leave { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + } + + .animate-if.ng-enter, + .animate-if.ng-leave.ng-leave-active { + opacity:0; + } + + .animate-if.ng-leave, + .animate-if.ng-enter.ng-enter-active { + opacity:1; + } + +
+ */ +var ngIfDirective = ['$animate', function($animate) { + return { + transclude: 'element', + priority: 600, + terminal: true, + restrict: 'A', + $$tlb: true, + link: function ($scope, $element, $attr, ctrl, $transclude) { + var block, childScope, previousElements; + $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { + + if (toBoolean(value)) { + if (!childScope) { + childScope = $scope.$new(); + $transclude(childScope, function (clone) { + clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); + // Note: We only need the first/last node of the cloned nodes. + // However, we need to keep the reference to the jqlite wrapper as it might be changed later + // by a directive with templateUrl when its template arrives. + block = { + clone: clone + }; + $animate.enter(clone, $element.parent(), $element); + }); + } + } else { + if(previousElements) { + previousElements.remove(); + previousElements = null; + } + if(childScope) { + childScope.$destroy(); + childScope = null; + } + if(block) { + previousElements = getBlockElements(block.clone); + $animate.leave(previousElements, function() { + previousElements = null; + }); + block = null; + } + } + }); + } + }; +}]; + +/** + * @ngdoc directive + * @name ngInclude + * @restrict ECA + * + * @description + * Fetches, compiles and includes an external HTML fragment. + * + * By default, the template URL is restricted to the same domain and protocol as the + * application document. This is done by calling {@link ng.$sce#getTrustedResourceUrl + * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols + * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or + * [wrap them](ng.$sce#trustAsResourceUrl) as trusted values. Refer to Angular's {@link + * ng.$sce Strict Contextual Escaping}. + * + * In addition, the browser's + * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) + * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) + * policy may further restrict whether the template is successfully loaded. + * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://` + * access on some browsers. + * + * @animations + * enter - animation is used to bring new content into the browser. + * leave - animation is used to animate existing content away. + * + * The enter and leave animation occur concurrently. + * + * @scope + * @priority 400 + * + * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, + * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`. + * @param {string=} onload Expression to evaluate when a new partial is loaded. + * + * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll + * $anchorScroll} to scroll the viewport after the content is loaded. + * + * - If the attribute is not set, disable scrolling. + * - If the attribute is set without value, enable scrolling. + * - Otherwise enable scrolling only if the expression evaluates to truthy value. + * + * @example + + +
+ + url of the template: {{template.url}} +
+
+
+
+
+
+ + angular.module('includeExample', ['ngAnimate']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.templates = + [ { name: 'template1.html', url: 'template1.html'}, + { name: 'template2.html', url: 'template2.html'} ]; + $scope.template = $scope.templates[0]; + }]); + + + Content of template1.html + + + Content of template2.html + + + .slide-animate-container { + position:relative; + background:white; + border:1px solid black; + height:40px; + overflow:hidden; + } + + .slide-animate { + padding:10px; + } + + .slide-animate.ng-enter, .slide-animate.ng-leave { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + + position:absolute; + top:0; + left:0; + right:0; + bottom:0; + display:block; + padding:10px; + } + + .slide-animate.ng-enter { + top:-50px; + } + .slide-animate.ng-enter.ng-enter-active { + top:0; + } + + .slide-animate.ng-leave { + top:0; + } + .slide-animate.ng-leave.ng-leave-active { + top:50px; + } + + + var templateSelect = element(by.model('template')); + var includeElem = element(by.css('[ng-include]')); + + it('should load template1.html', function() { + expect(includeElem.getText()).toMatch(/Content of template1.html/); + }); + + it('should load template2.html', function() { + if (browser.params.browser == 'firefox') { + // Firefox can't handle using selects + // See https://github.com/angular/protractor/issues/480 + return; + } + templateSelect.click(); + templateSelect.all(by.css('option')).get(2).click(); + expect(includeElem.getText()).toMatch(/Content of template2.html/); + }); + + it('should change to blank', function() { + if (browser.params.browser == 'firefox') { + // Firefox can't handle using selects + return; + } + templateSelect.click(); + templateSelect.all(by.css('option')).get(0).click(); + expect(includeElem.isPresent()).toBe(false); + }); + +
+ */ + + +/** + * @ngdoc event + * @name ngInclude#$includeContentRequested + * @eventType emit on the scope ngInclude was declared in + * @description + * Emitted every time the ngInclude content is requested. + */ + + +/** + * @ngdoc event + * @name ngInclude#$includeContentLoaded + * @eventType emit on the current ngInclude scope + * @description + * Emitted every time the ngInclude content is reloaded. + */ +var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate', '$sce', + function($http, $templateCache, $anchorScroll, $animate, $sce) { + return { + restrict: 'ECA', + priority: 400, + terminal: true, + transclude: 'element', + controller: angular.noop, + compile: function(element, attr) { + var srcExp = attr.ngInclude || attr.src, + onloadExp = attr.onload || '', + autoScrollExp = attr.autoscroll; + + return function(scope, $element, $attr, ctrl, $transclude) { + var changeCounter = 0, + currentScope, + previousElement, + currentElement; + + var cleanupLastIncludeContent = function() { + if(previousElement) { + previousElement.remove(); + previousElement = null; + } + if(currentScope) { + currentScope.$destroy(); + currentScope = null; + } + if(currentElement) { + $animate.leave(currentElement, function() { + previousElement = null; + }); + previousElement = currentElement; + currentElement = null; + } + }; + + scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) { + var afterAnimation = function() { + if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { + $anchorScroll(); + } + }; + var thisChangeId = ++changeCounter; + + if (src) { + $http.get(src, {cache: $templateCache}).success(function(response) { + if (thisChangeId !== changeCounter) return; + var newScope = scope.$new(); + ctrl.template = response; + + // Note: This will also link all children of ng-include that were contained in the original + // html. If that content contains controllers, ... they could pollute/change the scope. + // However, using ng-include on an element with additional content does not make sense... + // Note: We can't remove them in the cloneAttchFn of $transclude as that + // function is called before linking the content, which would apply child + // directives to non existing elements. + var clone = $transclude(newScope, function(clone) { + cleanupLastIncludeContent(); + $animate.enter(clone, null, $element, afterAnimation); + }); + + currentScope = newScope; + currentElement = clone; + + currentScope.$emit('$includeContentLoaded'); + scope.$eval(onloadExp); + }).error(function() { + if (thisChangeId === changeCounter) cleanupLastIncludeContent(); + }); + scope.$emit('$includeContentRequested'); + } else { + cleanupLastIncludeContent(); + ctrl.template = null; + } + }); + }; + } + }; +}]; + +// This directive is called during the $transclude call of the first `ngInclude` directive. +// It will replace and compile the content of the element with the loaded template. +// We need this directive so that the element content is already filled when +// the link function of another directive on the same element as ngInclude +// is called. +var ngIncludeFillContentDirective = ['$compile', + function($compile) { + return { + restrict: 'ECA', + priority: -400, + require: 'ngInclude', + link: function(scope, $element, $attr, ctrl) { + $element.html(ctrl.template); + $compile($element.contents())(scope); + } + }; + }]; + +/** + * @ngdoc directive + * @name ngInit + * @restrict AC + * + * @description + * The `ngInit` directive allows you to evaluate an expression in the + * current scope. + * + *
+ * The only appropriate use of `ngInit` is for aliasing special properties of + * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you + * should use {@link guide/controller controllers} rather than `ngInit` + * to initialize values on a scope. + *
+ *
+ * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make + * sure you have parenthesis for correct precedence: + *
+ *   
+ *
+ *
+ * + * @priority 450 + * + * @element ANY + * @param {expression} ngInit {@link guide/expression Expression} to eval. + * + * @example + + + +
+
+
+ list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}}; +
+
+
+
+ + it('should alias index positions', function() { + var elements = element.all(by.css('.example-init')); + expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;'); + expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;'); + expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;'); + expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;'); + }); + +
+ */ +var ngInitDirective = ngDirective({ + priority: 450, + compile: function() { + return { + pre: function(scope, element, attrs) { + scope.$eval(attrs.ngInit); + } + }; + } +}); + +/** + * @ngdoc directive + * @name ngNonBindable + * @restrict AC + * @priority 1000 + * + * @description + * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current + * DOM element. This is useful if the element contains what appears to be Angular directives and + * bindings but which should be ignored by Angular. This could be the case if you have a site that + * displays snippets of code, for instance. + * + * @element ANY + * + * @example + * In this example there are two locations where a simple interpolation binding (`{{}}`) is present, + * but the one wrapped in `ngNonBindable` is left alone. + * + * @example + + +
Normal: {{1 + 2}}
+
Ignored: {{1 + 2}}
+
+ + it('should check ng-non-bindable', function() { + expect(element(by.binding('1 + 2')).getText()).toContain('3'); + expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/); + }); + +
+ */ +var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); + +/** + * @ngdoc directive + * @name ngPluralize + * @restrict EA + * + * @description + * `ngPluralize` is a directive that displays messages according to en-US localization rules. + * These rules are bundled with angular.js, but can be overridden + * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive + * by specifying the mappings between + * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) + * and the strings to be displayed. + * + * # Plural categories and explicit number rules + * There are two + * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) + * in Angular's default en-US locale: "one" and "other". + * + * While a plural category may match many numbers (for example, in en-US locale, "other" can match + * any number that is not 1), an explicit number rule can only match one number. For example, the + * explicit number rule for "3" matches the number 3. There are examples of plural categories + * and explicit number rules throughout the rest of this documentation. + * + * # Configuring ngPluralize + * You configure ngPluralize by providing 2 attributes: `count` and `when`. + * You can also provide an optional attribute, `offset`. + * + * The value of the `count` attribute can be either a string or an {@link guide/expression + * Angular expression}; these are evaluated on the current scope for its bound value. + * + * The `when` attribute specifies the mappings between plural categories and the actual + * string to be displayed. The value of the attribute should be a JSON object. + * + * The following example shows how to configure ngPluralize: + * + * ```html + * + * + *``` + * + * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not + * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" + * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for + * other numbers, for example 12, so that instead of showing "12 people are viewing", you can + * show "a dozen people are viewing". + * + * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted + * into pluralized strings. In the previous example, Angular will replace `{}` with + * `{{personCount}}`. The closed braces `{}` is a placeholder + * for {{numberExpression}}. + * + * # Configuring ngPluralize with offset + * The `offset` attribute allows further customization of pluralized text, which can result in + * a better user experience. For example, instead of the message "4 people are viewing this document", + * you might display "John, Kate and 2 others are viewing this document". + * The offset attribute allows you to offset a number by any desired value. + * Let's take a look at an example: + * + * ```html + * + * + * ``` + * + * Notice that we are still using two plural categories(one, other), but we added + * three explicit number rules 0, 1 and 2. + * When one person, perhaps John, views the document, "John is viewing" will be shown. + * When three people view the document, no explicit number rule is found, so + * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. + * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing" + * is shown. + * + * Note that when you specify offsets, you must provide explicit number rules for + * numbers from 0 up to and including the offset. If you use an offset of 3, for example, + * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for + * plural categories "one" and "other". + * + * @param {string|expression} count The variable to be bound to. + * @param {string} when The mapping between plural category to its corresponding strings. + * @param {number=} offset Offset to deduct from the total number. + * + * @example + + + +
+ Person 1:
+ Person 2:
+ Number of People:
+ + + Without Offset: + +
+ + + With Offset(2): + + +
+
+ + it('should show correct pluralized string', function() { + var withoutOffset = element.all(by.css('ng-pluralize')).get(0); + var withOffset = element.all(by.css('ng-pluralize')).get(1); + var countInput = element(by.model('personCount')); + + expect(withoutOffset.getText()).toEqual('1 person is viewing.'); + expect(withOffset.getText()).toEqual('Igor is viewing.'); + + countInput.clear(); + countInput.sendKeys('0'); + + expect(withoutOffset.getText()).toEqual('Nobody is viewing.'); + expect(withOffset.getText()).toEqual('Nobody is viewing.'); + + countInput.clear(); + countInput.sendKeys('2'); + + expect(withoutOffset.getText()).toEqual('2 people are viewing.'); + expect(withOffset.getText()).toEqual('Igor and Misko are viewing.'); + + countInput.clear(); + countInput.sendKeys('3'); + + expect(withoutOffset.getText()).toEqual('3 people are viewing.'); + expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.'); + + countInput.clear(); + countInput.sendKeys('4'); + + expect(withoutOffset.getText()).toEqual('4 people are viewing.'); + expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.'); + }); + it('should show data-bound names', function() { + var withOffset = element.all(by.css('ng-pluralize')).get(1); + var personCount = element(by.model('personCount')); + var person1 = element(by.model('person1')); + var person2 = element(by.model('person2')); + personCount.clear(); + personCount.sendKeys('4'); + person1.clear(); + person1.sendKeys('Di'); + person2.clear(); + person2.sendKeys('Vojta'); + expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.'); + }); + +
+ */ +var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { + var BRACE = /{}/g; + return { + restrict: 'EA', + link: function(scope, element, attr) { + var numberExp = attr.count, + whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs + offset = attr.offset || 0, + whens = scope.$eval(whenExp) || {}, + whensExpFns = {}, + startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + isWhen = /^when(Minus)?(.+)$/; + + forEach(attr, function(expression, attributeName) { + if (isWhen.test(attributeName)) { + whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] = + element.attr(attr.$attr[attributeName]); + } + }); + forEach(whens, function(expression, key) { + whensExpFns[key] = + $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + + offset + endSymbol)); + }); + + scope.$watch(function ngPluralizeWatch() { + var value = parseFloat(scope.$eval(numberExp)); + + if (!isNaN(value)) { + //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, + //check it against pluralization rules in $locale service + if (!(value in whens)) value = $locale.pluralCat(value - offset); + return whensExpFns[value](scope, element, true); + } else { + return ''; + } + }, function ngPluralizeWatchAction(newVal) { + element.text(newVal); + }); + } + }; +}]; + +/** + * @ngdoc directive + * @name ngRepeat + * + * @description + * The `ngRepeat` directive instantiates a template once per item from a collection. Each template + * instance gets its own scope, where the given loop variable is set to the current collection item, + * and `$index` is set to the item index or key. + * + * Special properties are exposed on the local scope of each template instance, including: + * + * | Variable | Type | Details | + * |-----------|-----------------|-----------------------------------------------------------------------------| + * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) | + * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. | + * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. | + * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. | + * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). | + * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). | + * + * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}. + * This may be useful when, for instance, nesting ngRepeats. + * + * # Special repeat start and end points + * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending + * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively. + * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on) + * up to and including the ending HTML tag where **ng-repeat-end** is placed. + * + * The example below makes use of this feature: + * ```html + *
+ * Header {{ item }} + *
+ *
+ * Body {{ item }} + *
+ *
+ * Footer {{ item }} + *
+ * ``` + * + * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to: + * ```html + *
+ * Header A + *
+ *
+ * Body A + *
+ *
+ * Footer A + *
+ *
+ * Header B + *
+ *
+ * Body B + *
+ *
+ * Footer B + *
+ * ``` + * + * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such + * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**). + * + * @animations + * **.enter** - when a new item is added to the list or when an item is revealed after a filter + * + * **.leave** - when an item is removed from the list or when an item is filtered out + * + * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered + * + * @element ANY + * @scope + * @priority 1000 + * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These + * formats are currently supported: + * + * * `variable in expression` – where variable is the user defined loop variable and `expression` + * is a scope expression giving the collection to enumerate. + * + * For example: `album in artist.albums`. + * + * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers, + * and `expression` is the scope expression giving the collection to enumerate. + * + * For example: `(name, age) in {'adam':10, 'amalie':12}`. + * + * * `variable in expression track by tracking_expression` – You can also provide an optional tracking function + * which can be used to associate the objects in the collection with the DOM elements. If no tracking function + * is specified the ng-repeat associates elements by identity in the collection. It is an error to have + * more than one tracking function to resolve to the same key. (This would mean that two distinct objects are + * mapped to the same DOM element, which is not possible.) Filters should be applied to the expression, + * before specifying a tracking expression. + * + * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements + * will be associated by item identity in the array. + * + * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique + * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements + * with the corresponding item in the array by identity. Moving the same object in array would move the DOM + * element in the same way in the DOM. + * + * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this + * case the object identity does not matter. Two objects are considered equivalent as long as their `id` + * property is same. + * + * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter + * to items in conjunction with a tracking expression. + * + * @example + * This example initializes the scope to a list of names and + * then uses `ngRepeat` to display every person: + + +
+ I have {{friends.length}} friends. They are: + +
    +
  • + [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. +
  • +
+
+
+ + .example-animate-container { + background:white; + border:1px solid black; + list-style:none; + margin:0; + padding:0 10px; + } + + .animate-repeat { + line-height:40px; + list-style:none; + box-sizing:border-box; + } + + .animate-repeat.ng-move, + .animate-repeat.ng-enter, + .animate-repeat.ng-leave { + -webkit-transition:all linear 0.5s; + transition:all linear 0.5s; + } + + .animate-repeat.ng-leave.ng-leave-active, + .animate-repeat.ng-move, + .animate-repeat.ng-enter { + opacity:0; + max-height:0; + } + + .animate-repeat.ng-leave, + .animate-repeat.ng-move.ng-move-active, + .animate-repeat.ng-enter.ng-enter-active { + opacity:1; + max-height:40px; + } + + + var friends = element.all(by.repeater('friend in friends')); + + it('should render initial data set', function() { + expect(friends.count()).toBe(10); + expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.'); + expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.'); + expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.'); + expect(element(by.binding('friends.length')).getText()) + .toMatch("I have 10 friends. They are:"); + }); + + it('should update repeater when filter predicate changes', function() { + expect(friends.count()).toBe(10); + + element(by.model('q')).sendKeys('ma'); + + expect(friends.count()).toBe(2); + expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.'); + expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.'); + }); + +
+ */ +var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { + var NG_REMOVED = '$$NG_REMOVED'; + var ngRepeatMinErr = minErr('ngRepeat'); + return { + transclude: 'element', + priority: 1000, + terminal: true, + $$tlb: true, + link: function($scope, $element, $attr, ctrl, $transclude){ + var expression = $attr.ngRepeat; + var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/), + trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn, + lhs, rhs, valueIdentifier, keyIdentifier, + hashFnLocals = {$id: hashKey}; + + if (!match) { + throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.", + expression); + } + + lhs = match[1]; + rhs = match[2]; + trackByExp = match[3]; + + if (trackByExp) { + trackByExpGetter = $parse(trackByExp); + trackByIdExpFn = function(key, value, index) { + // assign key, value, and $index to the locals so that they can be used in hash functions + if (keyIdentifier) hashFnLocals[keyIdentifier] = key; + hashFnLocals[valueIdentifier] = value; + hashFnLocals.$index = index; + return trackByExpGetter($scope, hashFnLocals); + }; + } else { + trackByIdArrayFn = function(key, value) { + return hashKey(value); + }; + trackByIdObjFn = function(key) { + return key; + }; + } + + match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); + if (!match) { + throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.", + lhs); + } + valueIdentifier = match[3] || match[1]; + keyIdentifier = match[2]; + + // Store a list of elements from previous run. This is a hash where key is the item from the + // iterator, and the value is objects with following properties. + // - scope: bound scope + // - element: previous element. + // - index: position + var lastBlockMap = {}; + + //watch props + $scope.$watchCollection(rhs, function ngRepeatAction(collection){ + var index, length, + previousNode = $element[0], // current position of the node + nextNode, + // Same as lastBlockMap but it has the current state. It will become the + // lastBlockMap on the next iteration. + nextBlockMap = {}, + arrayLength, + childScope, + key, value, // key/value of iteration + trackById, + trackByIdFn, + collectionKeys, + block, // last object information {scope, element, id} + nextBlockOrder = [], + elementsToRemove; + + + if (isArrayLike(collection)) { + collectionKeys = collection; + trackByIdFn = trackByIdExpFn || trackByIdArrayFn; + } else { + trackByIdFn = trackByIdExpFn || trackByIdObjFn; + // if object, extract keys, sort them and use to determine order of iteration over obj props + collectionKeys = []; + for (key in collection) { + if (collection.hasOwnProperty(key) && key.charAt(0) != '$') { + collectionKeys.push(key); + } + } + collectionKeys.sort(); + } + + arrayLength = collectionKeys.length; + + // locate existing items + length = nextBlockOrder.length = collectionKeys.length; + for(index = 0; index < length; index++) { + key = (collection === collectionKeys) ? index : collectionKeys[index]; + value = collection[key]; + trackById = trackByIdFn(key, value, index); + assertNotHasOwnProperty(trackById, '`track by` id'); + if(lastBlockMap.hasOwnProperty(trackById)) { + block = lastBlockMap[trackById]; + delete lastBlockMap[trackById]; + nextBlockMap[trackById] = block; + nextBlockOrder[index] = block; + } else if (nextBlockMap.hasOwnProperty(trackById)) { + // restore lastBlockMap + forEach(nextBlockOrder, function(block) { + if (block && block.scope) lastBlockMap[block.id] = block; + }); + // This is a duplicate and we need to throw an error + throw ngRepeatMinErr('dupes', + "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}", + expression, trackById, toJson(value)); + } else { + // new never before seen block + nextBlockOrder[index] = { id: trackById }; + nextBlockMap[trackById] = false; + } + } + + // remove existing items + for (key in lastBlockMap) { + // lastBlockMap is our own object so we don't need to use special hasOwnPropertyFn + if (lastBlockMap.hasOwnProperty(key)) { + block = lastBlockMap[key]; + elementsToRemove = getBlockElements(block.clone); + $animate.leave(elementsToRemove); + forEach(elementsToRemove, function(element) { element[NG_REMOVED] = true; }); + block.scope.$destroy(); + } + } + + // we are not using forEach for perf reasons (trying to avoid #call) + for (index = 0, length = collectionKeys.length; index < length; index++) { + key = (collection === collectionKeys) ? index : collectionKeys[index]; + value = collection[key]; + block = nextBlockOrder[index]; + if (nextBlockOrder[index - 1]) previousNode = getBlockEnd(nextBlockOrder[index - 1]); + + if (block.scope) { + // if we have already seen this object, then we need to reuse the + // associated scope/element + childScope = block.scope; + + nextNode = previousNode; + do { + nextNode = nextNode.nextSibling; + } while(nextNode && nextNode[NG_REMOVED]); + + if (getBlockStart(block) != nextNode) { + // existing item which got moved + $animate.move(getBlockElements(block.clone), null, jqLite(previousNode)); + } + previousNode = getBlockEnd(block); + } else { + // new item which we don't know about + childScope = $scope.$new(); + } + + childScope[valueIdentifier] = value; + if (keyIdentifier) childScope[keyIdentifier] = key; + childScope.$index = index; + childScope.$first = (index === 0); + childScope.$last = (index === (arrayLength - 1)); + childScope.$middle = !(childScope.$first || childScope.$last); + // jshint bitwise: false + childScope.$odd = !(childScope.$even = (index&1) === 0); + // jshint bitwise: true + + if (!block.scope) { + $transclude(childScope, function(clone) { + clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' '); + $animate.enter(clone, null, jqLite(previousNode)); + previousNode = clone; + block.scope = childScope; + // Note: We only need the first/last node of the cloned nodes. + // However, we need to keep the reference to the jqlite wrapper as it might be changed later + // by a directive with templateUrl when its template arrives. + block.clone = clone; + nextBlockMap[block.id] = block; + }); + } + } + lastBlockMap = nextBlockMap; + }); + } + }; + + function getBlockStart(block) { + return block.clone[0]; + } + + function getBlockEnd(block) { + return block.clone[block.clone.length - 1]; + } +}]; + +/** + * @ngdoc directive + * @name ngShow + * + * @description + * The `ngShow` directive shows or hides the given HTML element based on the expression + * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding + * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined + * in AngularJS and sets the display style to none (using an !important flag). + * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). + * + * ```html + * + *
+ * + * + *
+ * ``` + * + * When the `ngShow` expression evaluates to false then the `.ng-hide` CSS class is added to the class attribute + * on the element causing it to become hidden. When true, the `.ng-hide` CSS class is removed + * from the element causing the element not to appear hidden. + * + *
+ * **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):
+ * "f" / "0" / "false" / "no" / "n" / "[]" + *
+ * + * ## Why is !important used? + * + * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector + * can be easily overridden by heavier selectors. For example, something as simple + * as changing the display style on a HTML list item would make hidden elements appear visible. + * This also becomes a bigger issue when dealing with CSS frameworks. + * + * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector + * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the + * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. + * + * ### Overriding `.ng-hide` + * + * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change + * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` + * class in CSS: + * + * ```css + * .ng-hide { + * //this is just another form of hiding an element + * display:block!important; + * position:absolute; + * top:-9999px; + * left:-9999px; + * } + * ``` + * + * By default you don't need to override in CSS anything and the animations will work around the display style. + * + * ## A note about animations with `ngShow` + * + * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression + * is true and false. This system works like the animation system present with ngClass except that + * you must also include the !important flag to override the display property + * so that you can perform an animation when the element is hidden during the time of the animation. + * + * ```css + * // + * //a working example can be found at the bottom of this page + * // + * .my-element.ng-hide-add, .my-element.ng-hide-remove { + * transition:0.5s linear all; + * } + * + * .my-element.ng-hide-add { ... } + * .my-element.ng-hide-add.ng-hide-add-active { ... } + * .my-element.ng-hide-remove { ... } + * .my-element.ng-hide-remove.ng-hide-remove-active { ... } + * ``` + * + * Keep in mind that, as of AngularJS version 1.2.17 (and 1.3.0-beta.11), there is no need to change the display + * property to block during animation states--ngAnimate will handle the style toggling automatically for you. + * + * @animations + * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible + * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden + * + * @element ANY + * @param {expression} ngShow If the {@link guide/expression expression} is truthy + * then the element is shown or hidden respectively. + * + * @example + + + Click me:
+
+ Show: +
+ I show up when your checkbox is checked. +
+
+
+ Hide: +
+ I hide when your checkbox is checked. +
+
+
+ + @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css); + + + .animate-show { + -webkit-transition:all linear 0.5s; + transition:all linear 0.5s; + line-height:20px; + opacity:1; + padding:10px; + border:1px solid black; + background:white; + } + + .animate-show.ng-hide { + line-height:0; + opacity:0; + padding:0 10px; + } + + .check-element { + padding:10px; + border:1px solid black; + background:white; + } + + + var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); + var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); + + it('should check ng-show / ng-hide', function() { + expect(thumbsUp.isDisplayed()).toBeFalsy(); + expect(thumbsDown.isDisplayed()).toBeTruthy(); + + element(by.model('checked')).click(); + + expect(thumbsUp.isDisplayed()).toBeTruthy(); + expect(thumbsDown.isDisplayed()).toBeFalsy(); + }); + +
+ */ +var ngShowDirective = ['$animate', function($animate) { + return function(scope, element, attr) { + scope.$watch(attr.ngShow, function ngShowWatchAction(value){ + $animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide'); + }); + }; +}]; + + +/** + * @ngdoc directive + * @name ngHide + * + * @description + * The `ngHide` directive shows or hides the given HTML element based on the expression + * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding + * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined + * in AngularJS and sets the display style to none (using an !important flag). + * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). + * + * ```html + * + *
+ * + * + *
+ * ``` + * + * When the `.ngHide` expression evaluates to true then the `.ng-hide` CSS class is added to the class attribute + * on the element causing it to become hidden. When false, the `.ng-hide` CSS class is removed + * from the element causing the element not to appear hidden. + * + *
+ * **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):
+ * "f" / "0" / "false" / "no" / "n" / "[]" + *
+ * + * ## Why is !important used? + * + * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector + * can be easily overridden by heavier selectors. For example, something as simple + * as changing the display style on a HTML list item would make hidden elements appear visible. + * This also becomes a bigger issue when dealing with CSS frameworks. + * + * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector + * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the + * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. + * + * ### Overriding `.ng-hide` + * + * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change + * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` + * class in CSS: + * + * ```css + * .ng-hide { + * //this is just another form of hiding an element + * display:block!important; + * position:absolute; + * top:-9999px; + * left:-9999px; + * } + * ``` + * + * By default you don't need to override in CSS anything and the animations will work around the display style. + * + * ## A note about animations with `ngHide` + * + * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression + * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide` + * CSS class is added and removed for you instead of your own CSS class. + * + * ```css + * // + * //a working example can be found at the bottom of this page + * // + * .my-element.ng-hide-add, .my-element.ng-hide-remove { + * transition:0.5s linear all; + * } + * + * .my-element.ng-hide-add { ... } + * .my-element.ng-hide-add.ng-hide-add-active { ... } + * .my-element.ng-hide-remove { ... } + * .my-element.ng-hide-remove.ng-hide-remove-active { ... } + * ``` + * + * Keep in mind that, as of AngularJS version 1.2.17 (and 1.3.0-beta.11), there is no need to change the display + * property to block during animation states--ngAnimate will handle the style toggling automatically for you. + * + * @animations + * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden + * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible + * + * @element ANY + * @param {expression} ngHide If the {@link guide/expression expression} is truthy then + * the element is shown or hidden respectively. + * + * @example + + + Click me:
+
+ Show: +
+ I show up when your checkbox is checked. +
+
+
+ Hide: +
+ I hide when your checkbox is checked. +
+
+
+ + @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css); + + + .animate-hide { + -webkit-transition:all linear 0.5s; + transition:all linear 0.5s; + line-height:20px; + opacity:1; + padding:10px; + border:1px solid black; + background:white; + } + + .animate-hide.ng-hide { + line-height:0; + opacity:0; + padding:0 10px; + } + + .check-element { + padding:10px; + border:1px solid black; + background:white; + } + + + var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); + var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); + + it('should check ng-show / ng-hide', function() { + expect(thumbsUp.isDisplayed()).toBeFalsy(); + expect(thumbsDown.isDisplayed()).toBeTruthy(); + + element(by.model('checked')).click(); + + expect(thumbsUp.isDisplayed()).toBeTruthy(); + expect(thumbsDown.isDisplayed()).toBeFalsy(); + }); + +
+ */ +var ngHideDirective = ['$animate', function($animate) { + return function(scope, element, attr) { + scope.$watch(attr.ngHide, function ngHideWatchAction(value){ + $animate[toBoolean(value) ? 'addClass' : 'removeClass'](element, 'ng-hide'); + }); + }; +}]; + +/** + * @ngdoc directive + * @name ngStyle + * @restrict AC + * + * @description + * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. + * + * @element ANY + * @param {expression} ngStyle + * + * {@link guide/expression Expression} which evals to an + * object whose keys are CSS style names and values are corresponding values for those CSS + * keys. + * + * Since some CSS style names are not valid keys for an object, they must be quoted. + * See the 'background-color' style in the example below. + * + * @example + + + + + +
+ Sample Text +
myStyle={{myStyle}}
+
+ + span { + color: black; + } + + + var colorSpan = element(by.css('span')); + + it('should check ng-style', function() { + expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); + element(by.css('input[value=\'set color\']')).click(); + expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)'); + element(by.css('input[value=clear]')).click(); + expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); + }); + +
+ */ +var ngStyleDirective = ngDirective(function(scope, element, attr) { + scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { + if (oldStyles && (newStyles !== oldStyles)) { + forEach(oldStyles, function(val, style) { element.css(style, '');}); + } + if (newStyles) element.css(newStyles); + }, true); +}); + +/** + * @ngdoc directive + * @name ngSwitch + * @restrict EA + * + * @description + * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression. + * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location + * as specified in the template. + * + * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it + * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element + * matches the value obtained from the evaluated expression. In other words, you define a container element + * (where you place the directive), place an expression on the **`on="..."` attribute** + * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place + * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on + * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default + * attribute is displayed. + * + *
+ * Be aware that the attribute values to match against cannot be expressions. They are interpreted + * as literal string values to match against. + * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the + * value of the expression `$scope.someVal`. + *
+ + * @animations + * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container + * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM + * + * @usage + * + * ``` + * + * ... + * ... + * ... + * + * ``` + * + * + * @scope + * @priority 800 + * @param {*} ngSwitch|on expression to match against ng-switch-when. + * On child elements add: + * + * * `ngSwitchWhen`: the case statement to match against. If match then this + * case will be displayed. If the same match appears multiple times, all the + * elements will be displayed. + * * `ngSwitchDefault`: the default case when no other case match. If there + * are multiple default cases, all of them will be displayed when no other + * case match. + * + * + * @example + + +
+ + selection={{selection}} +
+
+
Settings Div
+
Home Span
+
default
+
+
+
+ + angular.module('switchExample', ['ngAnimate']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.items = ['settings', 'home', 'other']; + $scope.selection = $scope.items[0]; + }]); + + + .animate-switch-container { + position:relative; + background:white; + border:1px solid black; + height:40px; + overflow:hidden; + } + + .animate-switch { + padding:10px; + } + + .animate-switch.ng-animate { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + + position:absolute; + top:0; + left:0; + right:0; + bottom:0; + } + + .animate-switch.ng-leave.ng-leave-active, + .animate-switch.ng-enter { + top:-50px; + } + .animate-switch.ng-leave, + .animate-switch.ng-enter.ng-enter-active { + top:0; + } + + + var switchElem = element(by.css('[ng-switch]')); + var select = element(by.model('selection')); + + it('should start in settings', function() { + expect(switchElem.getText()).toMatch(/Settings Div/); + }); + it('should change to home', function() { + select.all(by.css('option')).get(1).click(); + expect(switchElem.getText()).toMatch(/Home Span/); + }); + it('should select default', function() { + select.all(by.css('option')).get(2).click(); + expect(switchElem.getText()).toMatch(/default/); + }); + +
+ */ +var ngSwitchDirective = ['$animate', function($animate) { + return { + restrict: 'EA', + require: 'ngSwitch', + + // asks for $scope to fool the BC controller module + controller: ['$scope', function ngSwitchController() { + this.cases = {}; + }], + link: function(scope, element, attr, ngSwitchController) { + var watchExpr = attr.ngSwitch || attr.on, + selectedTranscludes = [], + selectedElements = [], + previousElements = [], + selectedScopes = []; + + scope.$watch(watchExpr, function ngSwitchWatchAction(value) { + var i, ii; + for (i = 0, ii = previousElements.length; i < ii; ++i) { + previousElements[i].remove(); + } + previousElements.length = 0; + + for (i = 0, ii = selectedScopes.length; i < ii; ++i) { + var selected = selectedElements[i]; + selectedScopes[i].$destroy(); + previousElements[i] = selected; + $animate.leave(selected, function() { + previousElements.splice(i, 1); + }); + } + + selectedElements.length = 0; + selectedScopes.length = 0; + + if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) { + scope.$eval(attr.change); + forEach(selectedTranscludes, function(selectedTransclude) { + var selectedScope = scope.$new(); + selectedScopes.push(selectedScope); + selectedTransclude.transclude(selectedScope, function(caseElement) { + var anchor = selectedTransclude.element; + + selectedElements.push(caseElement); + $animate.enter(caseElement, anchor.parent(), anchor); + }); + }); + } + }); + } + }; +}]; + +var ngSwitchWhenDirective = ngDirective({ + transclude: 'element', + priority: 800, + require: '^ngSwitch', + link: function(scope, element, attrs, ctrl, $transclude) { + ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []); + ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element }); + } +}); + +var ngSwitchDefaultDirective = ngDirective({ + transclude: 'element', + priority: 800, + require: '^ngSwitch', + link: function(scope, element, attr, ctrl, $transclude) { + ctrl.cases['?'] = (ctrl.cases['?'] || []); + ctrl.cases['?'].push({ transclude: $transclude, element: element }); + } +}); + +/** + * @ngdoc directive + * @name ngTransclude + * @restrict AC + * + * @description + * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion. + * + * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted. + * + * @element ANY + * + * @example + + + +
+
+
+ {{text}} +
+
+ + it('should have transcluded', function() { + var titleElement = element(by.model('title')); + titleElement.clear(); + titleElement.sendKeys('TITLE'); + var textElement = element(by.model('text')); + textElement.clear(); + textElement.sendKeys('TEXT'); + expect(element(by.binding('title')).getText()).toEqual('TITLE'); + expect(element(by.binding('text')).getText()).toEqual('TEXT'); + }); + +
+ * + */ +var ngTranscludeDirective = ngDirective({ + link: function($scope, $element, $attrs, controller, $transclude) { + if (!$transclude) { + throw minErr('ngTransclude')('orphan', + 'Illegal use of ngTransclude directive in the template! ' + + 'No parent directive that requires a transclusion found. ' + + 'Element: {0}', + startingTag($element)); + } + + $transclude(function(clone) { + $element.empty(); + $element.append(clone); + }); + } +}); + +/** + * @ngdoc directive + * @name script + * @restrict E + * + * @description + * Load the content of a ` + + Load inlined template +
+ + + it('should load template defined inside script tag', function() { + element(by.css('#tpl-link')).click(); + expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/); + }); + + + */ +var scriptDirective = ['$templateCache', function($templateCache) { + return { + restrict: 'E', + terminal: true, + compile: function(element, attr) { + if (attr.type == 'text/ng-template') { + var templateUrl = attr.id, + // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent + text = element[0].text; + + $templateCache.put(templateUrl, text); + } + } + }; +}]; + +var ngOptionsMinErr = minErr('ngOptions'); +/** + * @ngdoc directive + * @name select + * @restrict E + * + * @description + * HTML `SELECT` element with angular data-binding. + * + * # `ngOptions` + * + * The `ngOptions` attribute can be used to dynamically generate a list of `` + * DOM element. + * * `trackexpr`: Used when working with an array of objects. The result of this expression will be + * used to identify the objects in the array. The `trackexpr` will most likely refer to the + * `value` variable (e.g. `value.propertyName`). + * + * @example + + + +
+
    +
  • + Name: + [X] +
  • +
  • + [add] +
  • +
+
+ Color (null not allowed): +
+ + Color (null allowed): + + +
+ + Color grouped by shade: +
+ + + Select bogus.
+
+ Currently selected: {{ {selected_color:myColor} }} +
+
+
+
+ + it('should check ng-options', function() { + expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red'); + element.all(by.model('myColor')).first().click(); + element.all(by.css('select[ng-model="myColor"] option')).first().click(); + expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black'); + element(by.css('.nullable select[ng-model="myColor"]')).click(); + element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click(); + expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null'); + }); + +
+ */ + +var ngOptionsDirective = valueFn({ terminal: true }); +// jshint maxlen: false +var selectDirective = ['$compile', '$parse', function($compile, $parse) { + //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888 + var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/, + nullModelCtrl = {$setViewValue: noop}; +// jshint maxlen: 100 + + return { + restrict: 'E', + require: ['select', '?ngModel'], + controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) { + var self = this, + optionsMap = {}, + ngModelCtrl = nullModelCtrl, + nullOption, + unknownOption; + + + self.databound = $attrs.ngModel; + + + self.init = function(ngModelCtrl_, nullOption_, unknownOption_) { + ngModelCtrl = ngModelCtrl_; + nullOption = nullOption_; + unknownOption = unknownOption_; + }; + + + self.addOption = function(value) { + assertNotHasOwnProperty(value, '"option value"'); + optionsMap[value] = true; + + if (ngModelCtrl.$viewValue == value) { + $element.val(value); + if (unknownOption.parent()) unknownOption.remove(); + } + }; + + + self.removeOption = function(value) { + if (this.hasOption(value)) { + delete optionsMap[value]; + if (ngModelCtrl.$viewValue == value) { + this.renderUnknownOption(value); + } + } + }; + + + self.renderUnknownOption = function(val) { + var unknownVal = '? ' + hashKey(val) + ' ?'; + unknownOption.val(unknownVal); + $element.prepend(unknownOption); + $element.val(unknownVal); + unknownOption.prop('selected', true); // needed for IE + }; + + + self.hasOption = function(value) { + return optionsMap.hasOwnProperty(value); + }; + + $scope.$on('$destroy', function() { + // disable unknown option so that we don't do work when the whole select is being destroyed + self.renderUnknownOption = noop; + }); + }], + + link: function(scope, element, attr, ctrls) { + // if ngModel is not defined, we don't need to do anything + if (!ctrls[1]) return; + + var selectCtrl = ctrls[0], + ngModelCtrl = ctrls[1], + multiple = attr.multiple, + optionsExp = attr.ngOptions, + nullOption = false, // if false, user will not be able to select it (used by ngOptions) + emptyOption, + // we can't just jqLite('